株式会社Ninjastars
セキュリティエンジニア:一瀬健二郎
今回はAndroid ARM64でビルドされたcrackmeを対象に、lldbとlldb-serverを用いて動的解析を行います。
lldbの使い方を学べばiOSアプリケーションの解析にも応用でき、ARM64の理解はスマホアプリの脆弱性診断などでも重要です。
次世代の高機能デバッガであるlldbの使い方を学び診断に役立てましょう。
※本稿ではARM64については簡単に触れる程度にします。
環境
今回はlldbとlldb-serverを用いてリモートデバッグを行います。
lldbの都合上ホストOSはUbuntuを利用しますが、これは仮想マシン上でも可能です。
またAndroid実機が必要となりますが、root化している必要はありません。
Host:
Ubuntu 20.04LTS(VMWare上等でも可能)
LLDB-10
lldb-server(arm64-v8a AndroidStudio付属)
Guest:
Android実機(64bit)
※root化している必要はありません。
導入
Ubuntu上にlldbのインストール
sudo apt update sudo apt upgrade sudo apt install lldb-10
Ubuntu上にadbのインストール
sudo apt install adb
lldb-serverの取得
Download Android Studio and SDK tools | Android Developers
上記サイトからLinux(64-bit) android-studio-ide-xxxをダウンロードして展開してください。
android-studio/bin/lldb/android/arm64-v8aにlldb-serverが存在します。
このフォルダにTerminal上で移動してください。
またAndroid実機とPCをUSBで接続し、Androidの開発者オプションでUSBデバッグをオンにしてください。
adb push lldb-server /data/local/tmp adb shell chmod a+x /data/local/tmp/lldb-server
lldb-serverの起動
UbuntuのTerminal上で下記コマンドを入力することでAndroid実機上でlldb-serverがListen状態で起動します。
adb shell cd /data/local/tmp ./lldb-server platform --listen *:1234
lldbの起動とlldb-serverとのコネクト
UbuntuのTerminal上で下記コマンドを入力することでlldb-serverとコネクト出来ます。
lldb-10 #lldbが起動 platform select remote-android platform connect connect://localhost:1234
起動確認
lldbに以下コマンドを入力してみましょう。
platform process list
以下のように出力されれば成功です。
crackmeの解析
以下からcrackmeをダウンロードしてください。
drive.google.com
以下コマンドでcrackmeを起動できます。
#転送+権限付与 adb push crackme /data/local/tmp adb shell chmod a+x /data/local/tmp/crackme #起動 adb shell cd /data/local/tmp ./crackme
Please Input Key.と出力されるので、適当に入力すると終了してしまいます。
これを今からlldbとlldb-serverを用いて解析してみましょう。
まずcrackmeのプロセスIDを取得します。
platform process list
環境ごとに変わりますが、上の画像の場合20484がcrackmeのプロセスIDであると分かります。
それではlldbでcrackmeにアタッチしてみましょう。
#attach pid attach 20484
crackmeにアタッチ成功すると上のような状態になります。
lldbで現在のバックトレースを取得します。
thread backtrace
#もしくは下記
bt
バックトレースの結果からmain関数内でscanfが呼び出され、その内部でのreadが呼び出されてる状態であると分かります。
それではmain関数を逆アセンブルしてみましょう。
disassemble --name main #もしくは下記 di -n main
ここでx*やw*等の見慣れないレジスタが表示されています。
ARM64アーキテクチャの基本については下記の方のサイトが大変参考になります。ARM64について初見の方は下記のサイトでレジスタや命令について把握してください。
www.mztn.org
まずmain関数の逆アセンブル結果からstrlen関数を呼び出した後にx0レジスタと3を比較していることが分かります。
strlen関数は文字列長を返す関数であり、ARM64において戻り値は基本的にx0レジスタに格納されます。
main+60においてb.neのジャンプ先の次の命令がexitになっているため、文字列長が3でないとプログラムが終了するということが分かります。
よって文字列長は3であると判明しました。
次にmain+92においてw10レジスタとw8レジスタを比較し、一致していなければ次のb.neでwrongにジャンプすることが分かります。
つまりこのw10レジスタとw8レジスタを一致させるような入力を与えれば良いと分かります。
lldbから一旦crackmeを終了させます。
process kill
再度lldbでcrackmeにアタッチしてください。
main+92にブレークポイントをセットします。
breakpoint set --address main+92 #もしくは下記 br s -a main+92
停止中のプログラムを再開します。
continue #もしくは下記 c
この状態でcrackmeで適当に3文字"abc"と入力するとmain+92でブレークします。
lldbでレジスタの状態を取得します。
register read
w10レジスタ(x10レジスタの下位32bit)にはasciiコードで入力文字列であるabcが逆になったcba、w8レジスタ(x8レジスタの下位32bit)にはFLEという数値が格納されていることが分かります。
つまり入力文字列で"ELF"と与えれば良いということが分かります。
Flag:ninja{4nti_che4t_s0lution}
最後になりますが、lldbのコマンド等は下記公式サイトの一覧が分かりやすく纏まっています。
lldb.llvm.org
まとめ
lldbについて今回は簡潔に解説させていただきましたが、実際は非常に多機能に渡ります。
私自身も勉強中であり、常に情報をキャッチアップし脆弱性診断などに的確に利用して行きたいと思います。
本稿が読者の皆様の診断・学習の手助けとなれば幸いです。
注意事項
本レポートに記載されている内容を許可されていないソフトウェアで行うと、場合によっては犯罪行為となる可能性があります。そのため、記事の内容を試す際には許可されたソフトウェアに対してのみ実施するようにしてください。
本レポートについて
お問い合せ
E-mail:ichise@ninjastars-net.com
株式会社Ninjastarsエンジニア
一瀬健二郎