Sterra Security Tech Blog

株式会社ステラセキュリティの公式技術ブログです

パッチを当てるPythonスクリプトを生成するIDAプラグイン「genpatch」を作った話

取締役CTOの小竹(aka tkmru)です。

先月、genpatchというIDA Pro上で当てたパッチと同じパッチを当てるPythonスクリプトを生成するIDAプラグインを公開しました。 この記事では、作成した「genpatch」の使い方、仕組みを紹介します。

困っていたこと

スマホアプリが対象の脆弱性診断では、準備段階でリバースエンジニアリングを行い、パッチを当てることがあります。 SSL Pinning(Certificate Pinning、証明書のピン留め)をお客様側で無効化していただけなかった場合には、解析を行い、無効化するパッチを当てた上で、APIとの通信を診断します。 SSL Pinningは、使用するSSL証明書をアプリ内にハードコートし、通信時にSSL証明書がアプリ内のものか検証を行うことで、SSL通信の中間者攻撃を防ぐ仕組みです。 Burp Suiteなどのプロキシツールを使ってSSL通信を閲覧する際には、PCにインストールしたプロキシツールの証明書を使用しています。 そのため、SSL Pinningが実装されているアプリに対して、プロキシツールを使用して通信を閲覧しようとすると、SSL証明書の検証に失敗し、通信が行えません。

診断期間中に診断対象のアプリがアップデートされた場合、パッチを当て直す必要があります。 その際に、IDA Proを起動し、パッチを当て直す1のは面倒です。 また、IDA Proの操作に慣れていないチームメンバーにパッチを当ててもらうのも、大変です2。 そのため、IDA Pro上で当てたのと同じパッチを当てるPythonスクリプトを生成するプラグインを作成しました。

作ったツール

genpatchというIDA Pro上で当てたパッチと同じパッチを当てるPythonスクリプトを生成するIDAプラグインを作成しました。 genpatchが生成したパッチを当てるPythonスクリプトを使用することで、 繰り返しバイナリにパッチを当てる際に、Pythonスクリプトを実行するだけで済みます。 このプラグインPythonで実装しています。

github.com

インストール方法

リポジトリ内のgenpatch.pypatch_script_template.txtをIDA Pluginフォルダにコピーし、IDA Proを再起動するとgenpatchが使えるようになります。 Windowsの場合、コピー先のフォルダはC:\Program FilesIDA 8.x\pluginsです。 MacOSの場合、コピー先のフォルダは/Applications/IDA Pro 8.x/idaq.app/Contents/MacOS/pluginsにあります。

パッチを当てるPythonスクリプトを生成する

IDA Pro上でパッチを当てた後、Edit -> Plugins -> genpatchボタンをクリックすると、IDA Proで読み込んだバイナリがあるディレクトリにパッチスクリプトが生成されます。 スクリプトの名前は、解析中のバイナリに接尾辞 _patch.pyを付けたものになります。 例えば、バイナリ名が a.out の場合、パッチスクリプト名は a.out_patch.pyとなります。

Pythonスクリプトが正常に生成されると、以下のようなダイアログが表示されます。

生成したPythonスクリプトを実行する

コマンドライン第一引数にパッチを当てたいバイナリへのパスを指定して実行することで、パッチを当てることができます。

$ python a.out_patch.py <target_binary_path>

生成されたPythonスクリプトは次のようになります。16進数のデータ列を置換します。

#!/usr/bin/env python
# coding: UTF-8

import binascii
import os
import re
import sys

target_path = sys.argv[1]
target_data = None
with open(target_path, 'rb') as target_file:
    target_data = binascii.hexlify(target_file.read()).decode('utf-8')

# address: 0x100000ecb
# function name: __text: _main
# comment: Keypatch modified this from:   jz loc_100000EF6 Keypatch padded NOP to next boundary: 4 bytes
matches = re.findall('0f8425000000', target_data)
if len(matches) == 1:
    target_data = target_data.replace('0f8425000000', '752990909090')
else:
    print("Patch pattern isn't unique")
    sys.exit()

result_path = f'{target_path}_patched'
with open(result_path, 'wb') as result_file:
    if sys.version_info[0] >= 3:
        result_file.write(bytes.fromhex(target_data))
    else:
        result_file.write(target_data.decode('hex'))

print("Successfully generated patched binary to '%s'" % result_path)

おわりに

弊社ではこのように脆弱性診断中に遭遇した課題を解決するべく、適宜ツールを開発し、業界内に知見を共有していきます。 また、「genpatch」が脆弱性診断、マルウェア解析などリバースエンジニアリングを伴う仕事に従事するエンジニアに広く使われるよう育てていきたいと思っています。 ぜひ、気づいたことがあればフィードバックしてもらえるとうれしいです。 みなさまからのIssueやプルリクエストをお待ちしております。


  1. Unity製のスマホアプリではすべてのロジックがC++製の共有ライブラリに実装されるのでパッチを当てるのにIDA ProやGhidraを使う必要があります。Javaで実装されたAndroidアプリであればSmaliファイルを編集するだけで済みます。
  2. IDA Proのライセンスを用意するのも大変です。