mitmproxyでiOS Simulatorの特定URLへのHTTP/HTTPSの応答bodyを書換える


MacOS全体のプロキシ設定をローカルで起動したmitmproxyにセットして、iOS Simulator のHTTP/HTTPS通信を採取・変更するメモ。

mitmproxy の基本設定

mitmproxyをインストール (version 0.17.1):

$ brew install mitmproxy

初回起動時に ~/.mitmproxy/ 以下にCA証明書が生成される:

$ mitmproxy    # Ctrl-C で抜ける

Macのプロキシ設定をON/OFFする定型処理を足した下のスクリプトを mitmproxy-wrap などの名前で保存して実行できるようにする:

#!/bin/sh
# save as mitmproxy-wrap

sudo networksetup -setwebproxy Wi-Fi localhost 8080
sudo networksetup -setsecurewebproxy Wi-Fi localhost 8080
sudo networksetup -setwebproxystate Wi-Fi on
sudo networksetup -setsecurewebproxystate Wi-Fi on
mitmproxy "$@"
sudo networksetup -setwebproxystate Wi-Fi off
sudo networksetup -setsecurewebproxystate Wi-Fi off

iOS Simulator の証明書設定

XcodeでiOS Simulatorで1つ以上のデバイス(OS)を起動&終了しておく。
ADVTrustStoreにある iosCertTrustManager.py を使ってmitmproxyのCA証明書をiOS Simulatorの各デバイスにインストール:

$ ./iosCertTrustManager.py -a ~/.mitmproxy/mitmproxy-ca-cert.pem
subject= CN = mitmproxy, O = mitmproxy

Import certificate to iPhone 6 v10.0 [y/N] y
Importing to /Users/...(snip).../Library/Developer/CoreSimulator/Devices/...(snip).../data/Library/Keychains/TrustStore.sqlite3
  Existing certificate replaced
...(snip)...

mitmproxyでHTTP応答を書換える

フィルタのスクリプトを準備:

新しいmitmproxyの場合 (2.0.2で動作確認):

# Usage: -s "replace_response_body.py <url_prefix> <file>"
import argparse

class Replacer:
    def __init__(self, url_prefix, file_name):
        self.url_prefix, self.file_name = url_prefix, file_name

    def response(self, flow):
        flow.response.replace(self.url_prefix, self.file_name)
        if flow.request.pretty_url.startswith(self.url_prefix):
            with open(self.file_name, "r") as file:
                flow.response.text = file.read()

def start():
    parser = argparse.ArgumentParser()
    parser.add_argument("url_prefix", type=str)
    parser.add_argument("file_name", type=str)
    args = parser.parse_args()
    return Replacer(args.url_prefix, args.file_name)

古いmitmproxyの場合 (0.17で動作確認):

# Usage: -s "replace_response_body.py <url_prefix> <file>"
from mitmproxy.models import decoded

def start(context, argv):
    if len(argv) != 3:
        raise ValueError('Usage: -s "replace_response_body.py <url_prefix> <file>"')
    context.url_prefix, context.file = argv[1], argv[2]

def response(context, flow):
    if flow.request.pretty_url.startswith(context.url_prefix):
        with decoded(flow.response):
            with open(context.file, "r") as file:
                flow.response.content = file.read()

書換結果のファイルを準備:

// save as sample.js

alert('hello mitmproxy');

下は https://www.google-analytics.com/analytics.js で始まるURLのHTTPSの応答bodyを sample.js で書換えるサンプル:

$ mitmproxy-wrap -s "replace_response_body.py https://www.google-analytics.com/analytics.js sample.js"

iOS Simulatorを起動しSafariなどのブラウザで該当URLへのリクエストが発生すると、sample.jsが返却・実行されて下のダイアログが出る:

参考にしたページ