前回の続き。
qinstall.so に InstallUtil::getAvailableSize(QString) という関数があることが分かった。この辺をなんとかすればいけるかも?
InstallUtil::getAvailableSize の抽出
まず、この関数がどこからどこまでなのかを調べてみる。開始アドレスは前回の objdump の結果から、
00026d20 g DF .text 000001bc Base InstallUtil::getAvailableSize(QString)
0x26d20 だと分かっているので、そこから逆アセンブルしてみる。
$ arm-linux-objdump -d --start-address=0x26d20 qinstall.so | less
:
00026d20 <.text+0x14bd4>:
26d20: e1a0c00d mov ip, sp
26d24: e92ddc70 stmdb sp!, {r4, r5, r6, sl, fp, ip, lr, pc}
26d28: e24cb004 sub fp, ip, #4 ; 0x4
26d2c: e1a04000 mov r4, r0
26d30: e24b0020 sub r0, fp, #32 ; 0x20
:
そういえばARMはこんな感じだったっけ。
ARMでは関数の先頭で stmdb でレジスタを保存して、関数の最後に ldmdb でレジスタを復元してリターンするのが典型的なパターン。
そのまま先を見ていくと・・・。
:
26ed0: ea000000 b 0x26ed8
26ed4: 00000800 andeq r0, r0, r0, lsl #16
26ed8: e91bac70 ldmdb fp, {r4, r5, r6, sl, fp, sp, pc}
あった ldmdb。
脱線するけど、初めて ARM に触ったときにこの stmdb と ldmdb の仕組みが良くできてて感心した記憶がある。
関数の入り口と出口をならべてみるとよく分かる。
26d20: e1a0c00d mov ip, sp
26d24: e92ddc70 stmdb sp!, {r4, r5, r6, sl, fp, ip, lr, pc}
26ed8: e91bac70 ldmdb fp, {r4, r5, r6, sl, fp, sp, pc}
r4〜r6, sl, fp は素直に保存・復元が行われて、特に lr が見事で、コール時に保存した戻りアドレスの lr を pc に戻すことでリターンを実現している。
レジスタの復元とリターンが同時にできるなんて、なんてスマートなんだ!! しかも1命令で!! と感動したのは私だけ?? (^^;
・・・・話を戻すと、getAvailableSize の最後は 0x26ed8 で良さそう。
最適化を行うと、関数の途中にも ldmdb が出てきたりするんだけど、ジャンプを追いかけても 0x26d20〜0x26ed8 の間に収まってるみたい。
とりあえず関数の抽出ができた。
getAvailableSize の調査
本当にこの関数で statfs が呼ばれているのか調べてみる。
関数コールは bl 命令を探すだけでOK。
26d4c: ebffa9b6 bl 0x1142c
26d60: ebffaa05 bl 0x1157c
26d9c: ebffa7f2 bl 0x10d6c
26db8: ebffa9e3 bl 0x1154c
26dd0: ebffa995 bl 0x1142c
26de4: ebffa9e4 bl 0x1157c
26e1c: ebffa7d2 bl 0x10d6c
26e34: ebffa9c4 bl 0x1154c
26e48: ebffa9bf bl 0x1154c
26e50: ebffa6c1 bl 0x1095c
26e58: ebffa617 bl 0x106bc
26ec8: ebffa7a7 bl 0x10d6c
コール先がダブっているものを省くと、
26d4c: ebffa9b6 bl 0x1142c
26d60: ebffaa05 bl 0x1157c
26d9c: ebffa7f2 bl 0x10d6c
26db8: ebffa9e3 bl 0x1154c
26e50: ebffa6c1 bl 0x1095c
26e58: ebffa617 bl 0x106bc
この6つに絞られた。これを1つ1つ調べてみる。
まずは 0x1142c。このアドレスはどこかというと、
$ LANG=C arm-linux-readelf -S qinstall.so
There are 23 section headers, starting at offset 0x3918c:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
:
[10] .init PROGBITS 00010194 010194 000018 00 AX 0 0 4
[11] .plt PROGBITS 000101ac 0101ac 001fa0 04 AX 0 0 4
[12] .text PROGBITS 0001214c 01214c 020da4 00 AX 0 0 4
:
.plt セクションのようだ。.plt セクションは動的リンクしたライブラリへジャンプするためのコードが書かれたもの。
早速 0x1142c を逆アセンブルすると、
1142c: e59fc004 ldr ip, [pc, #4] ; 0x11438
11430: e08fc00c add ip, pc, ip
11434: e59cf000 ldr pc, [ip]
11438: 0002ef98 muleq r2, r8, pc
こんな感じのコードだった。C言語風に訳すと、
- ip = *((unsigned int *)0x11438);
- ip += 0x11438;
- pc = *((unsigned int *)ip) // ipに書かれたアドレスへジャンプ
つまりは、0x2ef98 と 0x11438 を足したアドレスに書かれているアドレスにジャンプするというもの。
足し合わせると、 0x403d0 となる。このアドレスは・・・
$ LANG=C arm-linux-readelf -S qinstall.so
There are 23 section headers, starting at offset 0x3918c:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
:
[17] .dtors PROGBITS 0003ff20 037f20 000008 00 WA 0 0 4
[18] .got PROGBITS 0003ff28 037f28 0010d0 04 WA 0 0 4
[19] .dynamic DYNAMIC 00040ff8 038ff8 0000d0 08 WA 3 0 4
:
.got セクションのようだ。.got セクションは、シンボル解決をしたアドレスが書かれるテーブル。
まぁ、白々しくこう書いたけども、.plt がジャンプコードになっていて、.got がそのジャンプアドレステーブルになっているのは ELF の動的リンクの仕組みそのもの。うん。いい勉強になるね。(また白々しい・・・)
で、例の 0x403d0 には何が書かれるかというと、
$ LANG=C arm-linux-objdump -RC qinstall.so | less
qinstall.so: file format elf32-littlearm
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
00032f5c R_ARM_ABS32 QTextStream::width(int)
:
000403cc R_ARM_JUMP_SLOT QCString::QCString(char const *)
000403d0 R_ARM_JUMP_SLOT QString::fromLatin1(char const *, int)
000403d4 R_ARM_JUMP_SLOT QFile::exists(void) const
:
QString::fromLatin1 のようだ。リンカがアドレス解決すると、0x403d0 に QString::fromLatin1 のアドレスが書き込まれる。
つまり、まとめると、bl 0x1142c は QString::fromLatin1 をコールしていたというわけだ。
といった感じで、残りのコール先も調べると、以下の様にになった。
26d4c: ebffa9b6 bl 0x1142c ; 0x403D0 QString::fromLatin1(char const *, int)
26d60: ebffaa05 bl 0x1157c ; 0x40424 QString::find(QString const &, int, bool) const
26d9c: ebffa7f2 bl 0x10d6c ; 0x40220 QStringData::deleteSelf(void)
26db8: ebffa9e3 bl 0x1154c ; 0x40418 QString::operator=(char const *)
26e50: ebffa6c1 bl 0x1095c ; 0x4011C QString::latin1(void) const
26e58: ebffa617 bl 0x106bc ; 0x40074 statfs
お、ちゃんと statfs を呼んでいるようだ。
qinstall.so 内で他に statfs を呼んでないかを調べてみる。
$ LANG=C arm-linux-objdump -d qinstall.so | grep 0x106bc
arm-linux-objdump: qinstall.so: no symbols
26e58: ebffa617 bl 0x106bc
うん。getAvailableSize 関数でしか呼んでないみたい。この関数でビンゴだ。
(まだまだ続く・・・)