GhidraでARM64をデコンパイルしたときのミスとその解析 | reverse-eg-mal-memoのブログ

reverse-eg-mal-memoのブログ

サイバーセキュリティに関して、あれこれとメモするという、チラシの裏的存在。
medium(英語):https://sachiel-archangel.medium.com/

なんか、ここのところセブンペイがらみのネタで、主に小波感を連発してきましたが。

本来は、マルウェア解析を主としたバイナリ解析のためのメモ・・・のはずなので、今回はちゃんと技術ネタでいきます。

 

ちょっと仕事で、ARMの解析をすることになりました。

もっとも、今回はマルウェアではなく、モジュールの安全性の確認です。

ただ、私もARMのアセンブラまではそんなに勉強しておらず・・・というので、デコンパイル機能があるGhidraの出番となりました。折角操作メモ書くくらいには習熟したんだもの、使い倒していかないとねw

 

その結果、便利なんですが問題が発生しました。

デコンパイルで、ミスがあったのです。

主に引数の解析で、そのパラメータの紐付きが取れないと解析が上手くいきません。今回はそのメモとなります。

なお、Ghidraは2019年の春に導入した「Version 9.0.2 Build PUBLIC 2019-Apr-03 1342 EDT」です。

将来的に、このエラーは修正される可能性があります。

 

 

Ghidraを使うメリット

 

改めて言うまでもないんですが、Ghidraの最大のメリットはデコンパイル機能です。

C言語ライクに変換してくれるため、C言語が読める人には、ほとんどの人にはこちらのほうが可読性が上がり、効率があがります。

え、あなたアセンブラの方が読みやすいんじゃないの!?

とか言われますが、別にそんなことはありません(・・・まあ、最近x86ならぶっちゃけそんな変わらん気もしてきたけど・・・)

 

静的解析ではとても便利な機能なのですが、デコンパイルコードにミスがあった場合、解析にも問題がでてしまいます。

そして、バイナリの実行形式ファイルのデコンパイルは、残念ながら完璧なものはありません。

(Javaに関しては、デコンパイルはコメント以外全部元にもどりますが、あれは厳密には実行用のバイナリではなく、中間言語的なものです。ただ、最近では難読化、デコンパイル対策もあるようで、イロイロカオスっぽいですね。)

 

今回は、解析していたら、ARM64のバイナリではデコンパイルミスがあり、見つけたというお話です。

 

 

デコンパイルしてみると

 

今回の記事のきっかけになったのはお仕事だったのですが、流石にお仕事のモジュールを晒すわけにもいきません。

そこで、ラズベリーパイの64bit版Linuxのモジュールを使って、同様の現象がおきないか確認してみました。

 

archlinux Raspberry Pi

https://wiki.archlinux.jp/index.php/Raspberry_Pi

 

このページにある以下のモジュール(ただし、2019年8月12日現在)をダウンロードしました。

http://os.archlinuxarm.org/os/ArchLinuxARM-rpi-3-latest.tar.gz
 

gz、tarを解凍すると、Linuxの実行モジュールがでてきます。

今回は、みなさんおなじみの cp コマンドをターゲットにしてみました。

Ghidraを起動、プロジェクトを作成し、/usr/binにある cp の実行モジュールを読み込ませ、解析します。解析のオプションはデフォルトのままとしています。

(モジュールを読ませて解析させるまでの手順は過去記事を参照。)

 

その結果を見てみると、やはりありました。

 

 

引数のパラメータが合わない・・・!?

 

例として、 FUN_001044f8 を見てみます。

39行目で、 FUN_0010c898 を引数なしでコールしています。

 

 

 

では、呼び出し先を見てみましょう。

 

 

 

・・・あれ?引数として、char *pcParm1を取ることになっている!?

でも、呼び出し側では引数が何かを指定いない・・・。

これでは、このコードを読む限りでは、何のパラメータを渡しているのかが分からず、関数の処理を解析しても、肝心の何のデータが処理されるのかが分かりません

 

このケースでは、呼び出される側の FUN_0010c898 のアセンブラコードを見ることで、パラメータが分かります。

 

 

 

この対比を見る限りでは、pcParm1は x0 レジスタの値だということが分かります。

このため、呼び出し元の FUN_001044f8 で、x0に何を設定しているかを確認することになります。

 

 

 

すると、直前のmemcpyの後、x0にパラメータを設定している様子はありません。

ARMは、サブルーチンをコールした際、原則としてはリターン値を x0 に設定しているようです。

このため、memcpyのリターン値をそのまま FUN_0010c898 の第1引数に渡すため、x0にパラメータを設定する処理自体が無いということになったようです。

この結果、このコードをデコンパイルするツールは引数なしと判定したのではないかと思われます。

どうも、直前の返り値をそのまま次の第1引数に渡すようなパターンのときに発生しているような気がします(あくまで私の感想・・・)

 

恐らく、デコンパイルの処理はシーケンシャルにやっていて、関数に当たった際に関数から先に解析し、その関数のプロトタイプを解析(要は、関数の引数や返り値をC言語のヘッダファイルのプロトタイプのように解析)してから、呼び出し元のパラメータ解析をやっているわけではないようですね。

関数がどんな引数を取るかわからないまま解析してしまうので、このようなケースでミスしやすいのかもしれません。

 

このため、こういったケースがあるということを知った上で、実際には必要な引数パラメータがどこから来るかを判断する必要があること、その導き方は知っておく必要がある、ということで、今回のメモ作成となりました。

 

 

ついでにメモメモにゃ!

 

原因と真のパラメータの求め方が分かったのですが。

折角解析しても、大体人間って忘れますよね・・・。

ということで、解析結果もメモしておきましょう、という話です。

Ghidraの機能紹介でも触れた、コメント機能が役に立ちます。

コメント機能で引数について、最低でも自分には分かるように覚え書きをしておきましょう。

 

 

 

 

お仕事では、gdbで解析する際に、Ghidraから得られる情報やらを使ってやってみたところ、割といい感じでした。

この手順については、また折を見てメモしたいと思います。

思うだけで、ほんとにするかは分からないので、期待しないで待っていてください。