Jailbreakアプリ開発のススメ

この記事は、[ドワンゴ Advent Calendar 2018](https://qiita.com/advent-calendar/2018/dwango)の17日目です。

<!--
昨日は　NAME さんの、 TITLE でした。
-->

@i4M1k0SU です。音ゲーマーです。
会社では、ニコニコ動画のスマートフォン向けWebサービスの開発を行なっています。

せっかくなのでAC駆動開発でもしようと思いましたが、忙しくて思ったように時間が取れなかったので、このような記事になってしまいました。

# はじめに
Jailbreak(脱獄)と呼ばれる端末のrootを取得する行為は初代iPhoneが発売された2007年ごろから行われて来ました。
当時のiOS(正式にはiPhone OS)は機能も貧弱でしたので、Jailbreakしないとまともに使えたものではなかったくらいでした。
近年はiOSの機能も充実してきたので、あえてJailbreakを行うメリットがないと言われることが多くなって来ています。
しかし、それでもなお、痒いところに手の届くJailbreakは一定数のユーザーが行なっており未だにJailbreak手法の解析・開発やJailbreakされた端末上で動くアプリの開発が行われ続けています。

*Jailbreakは改造行為です。メーカー保証範囲外となる行為ですので、自己責任でお願いします。*

## Jailbreak方法
本記事の趣旨と異なるため扱いません。
各自で調べ自己責任でお願いします。

## 開発環境構築
`theos` というものを使います。
MacでもWinでもLinuxでもiOSでも可能。
[こちら](https://github.com/theos/theos/wiki)の手順書を参考に簡単に構築できます。


# Jailbreakで触ることのできる範囲
- iOSの挙動
    - iOSのデフォルトの挙動に処理を追加する・書き換える
    - daemonを追加する・動かす
- AppStore/プリインストールアプリの挙動
    - アプリデフォルトの挙動に処理を追加する・書き換える

# 仕組み的なもの
MacOS/iOSの実行ファイルはmach-oバイナリというもので、実行ファイルからクラス・メソッドを確認できるヘッダーを取り出すことができます。
dylibインジェクションを行い既存のメソッドにオブジェクト指向で言う継承の要領で処理を追加・書き換えすることで挙動を変えることができ、これをtweakと呼ばれています。
Objective-Cで書かれたアプリもSwiftで書かれたアプリもtweakを作る際はObjective-Cを使用します。
アプリもiOSも同じmach-oバイナリなのでほぼ同じ方法で挙動を書き換えることができます。

# 実際のやり方
以下はiPhone Xのステータスバーにある時計の値を書き換えるサンプルです。(厳密には、 `_UIStatusBarStringView` は4Gなどの表示にも使われているのでその部分も書き換わる。)

```objc
%hook _UIStatusBarStringView
- (void)setText:(NSString *)arg1 {
    NSString *text = @"hoge";
    %orig(text);
}
%end
```

このように `hoge` になっていることがわかります。
![IMG_3347.jpg](https://qiita-image-store.s3.amazonaws.com/0/151628/8631f694-fc38-fa6f-9f91-e8521c1a538f.jpeg)

これは `_UIStatusBarStringView` というクラス内にある `- (void)setText:(NSString *)arg1`というメソッドを書き換え常に `hoge` が引数に指定されている状態にしているということです。
`%orig(arg)` を使うと既存の処理に違った引数を渡し、実行することができます。



```objc
%hook _UIStatusBarStringView
- (void)setText:(NSString *)arg1 {
    // 既存処理の前
    %orig;// 既存の処理を実行する
    // 既存処理の後
}
%end
```

またこのように引数をつけない `%orig` を使うと既存の処理をそのまま実行することができ、その前後に任意処理を追加することもできます。
（当然ながら、既存の処理内部の一部分を書き換えたい場合は、逆アセなどで内部の処理を全てトレースした上で書き換える必要があり、難易度が上がります。）

細かい文法やそのほかの作法などは、[ここ](http://iphonedevwiki.net/index.php/Logos)に書かれています。

theosそのものの使い方などは調べるといくらでも出てくる情報なので、ここでは割愛します。

# 書き換えるクラス・メソッドの探し方
メソッドの処理を書き換えることでなんでもできるように見えますが、それを行うためにはまずその書き換え対象を見つける作業が必要です。
`class-dump` というツールを使うことで暗号化されてない実行ファイルであれば、mach-oバイナリからヘッダーを取り出すことができます。
brewコマンドで簡単にインストールできます。

```shell
$ brew install class-dump
```

ヘッダーファイルからクラス名とメソッド名がわかるのでそこから処理を推測し、実際に書き換えてみるという方法が王道です。

また、iOSそのもののクラス名、メソッド名を調べたいときは、[こちら](http://developer.limneos.net/)が有効です。
class-dumpをする必要もないですし、検索もWeb上でできてしまいます。

しかし名前からの推測ではわかりにくいことがあり、その場合、メソッドの既存の処理はそのままに、ログ出力機能を追加し、処理を探るという方法を行います。

```objc
%hook _UIStatusBarStringView
- (void)setText:(NSString *)arg1 {
    %log;// 引数に渡された値などをログを出力する
    %orig;// 既存の処理を実行する
}
%end
```

アタリをつけたいメソッドにこのような書き方でログ出力を行い、その結果から目的のメソッドを見つけることができます。

これでも難しい場合は逆アセして探ることになりますが、大抵の場合はこの手順の繰り返しにより見つけることができます。

--------

私がたまに趣味で行なっているtweakの作り方の記事でした。
iOSアプリ及びiOSがどのように作られているのかといったことがある程度は読み取れてとても面白いので、興味のある方は是非チャレンジしていただきたいと思います。（自己責任でお願います。）

過去に作ったtweakを適当に紹介して終わりとさせていただきます。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">そういえばiOS純正のメールアプリで全件未読にする事故を防ぐTweak、Mark as Unread AlertをBigBossにてリリースしました <a href="https://t.co/hWANJPfBmJ">pic.twitter.com/hWANJPfBmJ</a></p>&mdash; みこす(空集合) (@i4M1k0SU) <a href="https://twitter.com/i4M1k0SU/status/873915241461014528?ref_src=twsrc%5Etfw">2017年6月11日</a></blockquote>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">iPhoneXの左上に空きRAMを表示するTweakを作りました <a href="https://t.co/oVydwa7kzf">pic.twitter.com/oVydwa7kzf</a></p>&mdash; みこす(空集合) (@i4M1k0SU) <a href="https://twitter.com/i4M1k0SU/status/1027121647944257537?ref_src=twsrc%5Etfw">2018年8月8日</a></blockquote>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">また変なTweak作りました <a href="https://t.co/Lg1j1DJMvc">pic.twitter.com/Lg1j1DJMvc</a></p>&mdash; みこす(空集合) (@i4M1k0SU) <a href="https://twitter.com/i4M1k0SU/status/787683770631458816?ref_src=twsrc%5Etfw">2016年10月16日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>