仙石浩明の日記

2010年11月27日

月額8円で運用できる Android 端末 IS01 で、root 権限が必要なアプリを使えるようにしてみた

Android 搭載スマートブック IS01 by SHARP (愛称 「メガネケース」) が、 本体価格 0円 + 契約事務手数料 2835円 + 月々 8円 * 2年しばり = 3027円 で入手できるとネット上で話題になっていたので買ってみた。 メジャーアップデート対応なしと公式に宣告されてしまった IS01 ではあるが、 「ワンセグ放送が視聴できて WiFi 通信もできるポメラもどき」 と割りきったとしても激安なので 「ふたつでじゅうぶんですよ」 と言われつつ 3つ入手した。

「グローバル展開もしていないから xda の助けも得られない」 と総統閣下に嘆かれつつも、 RageAgainstTheCage exploit を利用すれば root になることは一応できる (ベースバンドバージョン / ビルド番号 01.00.07 で確認):

senri:/home/sengoku % adb push rageagainstthecage /data/local/tmp/
senri:/home/sengoku % adb shell
$ cd /data/local/tmp
$ chmod 755 rageagainstthecage
$ ./rageagainstthecage
[*] CVE-2010-EASY Android local root exploit (C) 2010 by 743C

[*] checking NPROC limit ...
[+] RLIMIT_NPROC={1856, 1856}
[*] Searching for adb ...
[+] Found adb as PID 8223
[*] Spawning children. Dont type anything and wait for reset!
[*]
[*] If you like what we are doing you can send us PayPal money to
[*] 7-4-3-C@web.de so we can compensate time, effort and HW costs.
[*] If you are a company and feel like you profit from our work,
[*] we also accept donations > 1000 USD!
[*]
[*] adb connection will be reset. restart adb server on desktop and re-login.
$ 
senri:/home/sengoku % adb shell
# 

ただし rageagainstthecage はタイミングが良くないと失敗するので、 少なくとも数回は繰り返し実行しないと root が取れない (運が悪いと何度実行しても成功しない上に adb で接続できなくなって再起動する羽目に陥ることも)。 IS01 を再起動するたびに rageagainstthecage を実行して root を取り直すのは著しく手間なので、 root が取れたら su コマンドをインストールしておくべき。

インストール先は、 一般ユーザ (shell 権限) でアクセス可能で、 かつ再起動しても消えない (つまり tmpfs とかでない) ボリュームで、 かつ mount 時に nosuid オプションをつけられていないことが必須条件になるが、 幸い IS01 ではフラッシュメモリ /dev/block/mtdblock7 が /sqlite_journals に nosuid 無しで mount されている:

senri:/home/sengoku % adb shell
$ grep -v '\(tmpfs\|nosuid\)' /proc/mounts
rootfs / rootfs ro 0 0
devpts /dev/pts devpts rw,mode=600 0 0
proc /proc proc rw 0 0
sysfs /sys sysfs rw 0 0
/dev/block/mtdblock7 /sqlite_journals yaffs2 rw 0 0
/dev/block/mtdblock5 /system yaffs2 ro 0 0
$ ls -l /sqlite_journals
drwxrwx--x system   system            1980-01-06 09:00 com.android.providers.settings
drwxrwx--x system   system            1980-01-06 09:00 com.android.providers.settingsex
drw-rw-rw- root     root              2010-11-26 18:51 lost+found

sqlite_journals というディレクトリ名からして SQLite 用のディレクトリと思われるが、 それならなぜ nosuid がつけられていないのだろうか? ガチガチに防御を固めているはずの IS01 にしては珍しい抜け穴の一つ。

ちなみに上記のように /dev/block/mtdblock5 も /system ディレクトリに nosuid オプション無しでマウントされているが、 こちらはカーネルの MTD ドライバで書き込みが禁止されている (SHARP が公開している IS01 のカーネル kernel/drivers/mtd/devices/msm_nand.c 参照) ので、 簡単には変更できそうにない (少なくとも rw で remount するだけでは書込めない)。

というわけで /sqlite_journals に bin ディレクトリを作って ChainsDD の su コマンド (android_system_extras に含まれる) を置いてみる。

# ls -l /sqlite_journals/bin/su
-rwsr-sr-x    1 root     root         26256 Aug 17 17:27 /sqlite_journals/bin/su

ところが!

senri:/home/sengoku % adb shell
$ /sqlite_journals/bin/su
su: permission denied

su コマンドにはちゃんと setuid ビットを立てているのに、 なぜ 「permission denied」? と思って Android のエラーログを見てみると、

senri:/home/sengoku % adb logcat
	...
E/su      ( 4804): Getting exe path failed with 13: Permission denied
E/su      ( 4804): Getting exe path failed with 13: Permission denied
W/su      ( 4804): request rejected (2000->0 /system/bin/sh)

このエラーを出しているのは、 android_system_extras/su/su.c の 100行目あたり:

    /* If this isn't app_process, use the real path instead of argv[0] */
    snprintf(path, sizeof(path), "/proc/%u/exe", from->pid);
    len = readlink(path, exe, sizeof(exe));
    if (len < 0) {
        PLOGE("Getting exe path");
        return -1;
    }
    exe[len] = '\0';
    if (strcmp(exe, "/system/bin/app_process")) {
        argv0 = exe;
    }

えっ? 「/proc/*/exe」 のリンクが読めないってこと?

# echo $$
5374
# ls -l /proc/*/exe
ls: /proc/1/exe: cannot read link: Permission denied
lrwxrwxrwx    1 root     root             0 Nov 27 10:50 /proc/1/exe
	...
ls: /proc/5374/exe: cannot read link: Permission denied
lrwxrwxrwx    1 root     root             0 Nov 27 08:37 /proc/5374/exe
	...
# 

うむ〜 root 権限を持っていても root のシンボリックリンクを読めないとわ...
どこまで面倒くさいカーネルなんだ > SHARP

root 権限で /proc/*/exe を読めないのは困ったものだが、 su コマンドが /proc/*/exe を読もうとするのは su コマンドの呼び出し元の cmdline の argv[0] の代りに 「real path」 を調べるためなので、 su コマンドをアプリから呼ぶ限りにおいては、 この処理をスキップしてしまって特に問題はない。

su.c から /proc/*/exe を読もうとする部分 (上で引用した部分) をばっさり削除して su コマンドをビルドし直す:

senri:/usr/local/src/Android/donut % git clone -b donut git://github.com/ChainsDD/android_system_extras.git
senri:/usr/local/src/Android/donut % sh
$ . build/envsetup.sh
including vendor/aosp/vendorsetup.sh
$ cd android_system_extras
$ mm
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=1.6
TARGET_PRODUCT=generic
TARGET_BUILD_VARIANT=eng
TARGET_SIMULATOR=
TARGET_BUILD_TYPE=release
TARGET_ARCH=arm
HOST_ARCH=x86
HOST_OS=linux
HOST_BUILD_TYPE=release
BUILD_ID=Donut
============================================
make: Entering directory `/usr/local/src/Android/donut'
target thumb C: perm_checker <= /usr/local/src/Android/donut/android_system_extras/tests/fstest/perm_checker.c
	...
Install: out/target/product/generic/system/xbin/su
	...

できた out/target/product/generic/system/xbin/su を IS01 の /sqlite_journals/bin/su へコピーし、 chown root:root および chmod 6755 する。 Android マーケットから ChainsDD の Superuser アプリをインストールしておいて、 /sqlite_journals/bin/su を実行すれば、

Superuser on IS01

↑ のような画面が表示がされるので、 「許可」 ボタンを押せばよい。 「記憶する」 にチェックをつけておけば、 su コマンドの呼び出し元 (今回はコマンドラインから呼び出しているので /system/bin/sh) が同一である限り、 ユーザへの問合わせ無しに自動的に許可される。

さて、 これで su コマンドが使えるようになったが、 アプリから su コマンドを呼び出すには PATH が通っているところ、 すなわち /system/bin 以下に su コマンドを置く必要がある。

ところが root 権限があっても /system 以下には書込めないのが IS01 の困ったところ。 仕方ないので私は以下のような shell スクリプトを実行して /system を umount し、 /system ディレクトリを作って /system/bin を /sqlite_journals/bin へのシンボリックリンクにしている。

#!/system/bin/sh
export PATH=/sqlite_journals/xbin:/system/bin
mount -w -oremount null /
mkdir /system.org
mkdir /system.new
mount -obind /system /system.org
cd /system.org
for f in [a-o]* pubkey [q-z]*; do
    ln -s /system.org/$f /system.new/$f
done
cd /
umount -l /system; rmdir /system; mv /system.new /system
ln -snf /sqlite_journals/xbin /system/xbin
ln -snf /sqlite_journals/bin /system/bin

これで /system/bin が書込み可能になった。 元々 /system/bin 以下にあったコマンドを /sqlite_journals/bin へコピーしておけばよい。

Filed under: Android — hiroaki_sengoku @ 15:46

12件のコメント »

  1. 署名募集中 – IS01 に Android2.2 を!

    月々実質8円で運用できると話題のキーボー…

    トラックバック by 侍ズム — 2010年11月28日 @ 12:57

  2. IS01のビルド番号 01.00.09なのですが、
    /sqlite_journals/bin に入れたsuコマンドを実行することができないです。  

    コメント by kako — 2010年11月30日 @ 03:12

  3. 「grep /sqlite_journals /proc/mounts」と
    「ls -l /sqlite_journals/bin/su」の実行結果はどうなります?

    # grep /sqlite_journals /proc/mounts
    /dev/block/mtdblock7 /sqlite_journals yaffs2 rw 0 0

    # ls -l /sqlite_journals/bin/su
    -rwsr-sr-x 1 root root 26228 Nov 27 13:55 /sqlite_journals/bin/su

    コメント by hiroaki_sengoku — 2010年11月30日 @ 10:10

  4. IS01を再起動してsuが動作しない状態のまま、termnalで
    ls -l /sqlite_journals/bin/su
    を実行してみました。
    結果は
    -rwsr-sr-x shell shell 26228 2010-11-27 18:17 su
    となりました。

    あと、grepは実行できないので
    cat /proc/mountsをみました
    /dev/block/mtdplock7 /sqlite_journals yaffs2 rw 0 0
    となっていました。

    ご参考になりましたら幸いです。

    コメント by kako — 2010年11月30日 @ 12:15

  5. ownerの設定していなかったのが問題のようで

    rageagainstthecageを動かしたあと、
    # cd /sqlite_journals/bin
    # chown root su
    # chmod 6755 su
    として設定して、再起動後にsuコマンドを動作させることができました。

    これを試す時に、デバッグモードを有効にするのを忘れていて
    rageagainstthecageが動かなくなるという現象で、原因に気が付かず、
    結果をここに書くのが遅くなりました。

    コメント by kako — 2010年11月30日 @ 15:10

  6. なるほど。chown root に言及するのを忘れていました。
    ご指摘に感謝します。

    IS01 の /sqlite_journals/bin/su へコピーし、
    chmod 6755 する

    IS01 の /sqlite_journals/bin/su へコピーし、
    chown root:root および chmod 6755 する

    に修正しました。

    コメント by hiroaki_sengoku — 2010年11月30日 @ 15:22

  7. is03はできないですよね
    今度試してみます

    コメント by fd — 2010年12月3日 @ 23:16

  8. 初めましてIS01とwpa2-EAPで検索してここに来ました。
    実はIS01を使用していて、wpa2-EAPに繫げられず困っています。
    もしできるのならばその方法をお教えて貰えませんか?
    自分で試した方法は
    mobilehackerzのIS01root.apkをインストール
    ChainsDD の Superuser をインストール
    Fan ZhangのWifi Helper ver.1.1.1をインストール
    wifi helperでNET名,wpq2 enterprise, ID, passを設定して保存
    本体の通信設定でNET名から繋ごうとしてまた、ID,passを入力するも
    繋がる気配がありません。
    (恐らく証明書が必要なのでしょうが、ここから先どうして良いか分かりません)

    よろしくお願いします。

    コメント by kk — 2010年12月4日 @ 05:11

  9. Android 1.6 で WPA2 EAP は難易度が高いですね。
    まずは (1.6以外の) 確実に EAP 接続できるクライアントで
    接続できるか確認することから始めてみてはいかがでしょうか?

    ちなみに Fan Zhang の Wifi Helper って、
    Android Market に登録されている 1.1.1 は、
    *This App does not work ANY MORE*
    と書いてあるようですが…

    コメント by hiroaki_sengoku — 2010年12月7日 @ 09:08

  10. 初めまして。IS03で/system以下への書き込みの方法を探してここに辿り着きました。
    root化は既に済んでいますが、suにpathを通せなくて困っていました。
    最後のshellスクリプトと同じ手がIS03でも使えそうだと思ったのですが、
    このスクリプトは端末を再起動させるたびに実行させる必要があるという認識でいいのでしょうか?
    言い換えれば、何かあっても電源を入れなおせば元に戻ることが期待できるかということにもなるのですが。

    コメント by Isawo-Kikuchi — 2011年5月3日 @ 00:23

  11. ご認識の通りだとは思いますが、あくまで自己責任でお願いします。
    つまり何が起きても、ご自身の責任において対処していただきたいと思いますし、私としては一切の責任を負えません。
    特に、IS03 など私が持っていない端末に関しては、トラブル復旧のお手伝いがしたくても、原因究明のすべがありませんので (持ってる端末ならお手伝いするかというと、それは時と場合によりけり… ^^;)。

    コメント by hiroaki_sengoku — 2011年5月3日 @ 15:57

  12. お返事ありがとうございます。
    すでに試させて頂きましたが、IS03は/systemをアンマウントできなくなっているようで。そこはなんとか回避して/systemをいじれるようになったのですが、今度は「不正なアプリケーションが~」と出て再起動を余儀なくされたりと、苦戦中です。ご報告まで。

    コメント by Isawo-Kikuchi — 2011年5月3日 @ 16:26

この投稿へのコメントの RSS フィード。

コメントする