Upgrade to Pro — share decks privately, control downloads, hide ads and more …

アプリをエミュレートするアプリの登場とその危険性 / How multi-account app works #DroidKaigi

アプリをエミュレートするアプリの登場とその危険性 / How multi-account app works #DroidKaigi

ここ数年でいくつかの、他のAndroidアプリをエミュレートするAndroidアプリが登場しています(Parallel Space, GO Multipleなど)。その多くがLINEなどのメッセージングアプリやゲームなどを2アカウント同時使用できることを謳っており、多くのユーザーに使われていると思われます。
本セッションではこれらのアプリがどのようにして他のアプリをエミュレートしているのか調査した結果と、これらのアプリを使用することによる危険性の分析について発表を行います。

Takaki Hoshikawa

February 09, 2018
Tweet

More Decks by Takaki Hoshikawa

Other Decks in Technology

Transcript

  1. #DroidKaigi_room5
    アプリをエミュレートするアプリの登場と
    その危険性
    2018/02/09 DroidKaigi 2018
    エムスリー株式会社 星川貴樹 (@oboenikui)

    View Slide

  2. #DroidKaigi_room5
    # 自己紹介
    ## 名前
    星川貴樹 Takaki Hoshikawa
    ## ニックネーム
    @oboenikui
    ## 会社
    エムスリー株式会社(新卒1年目)
    主にAndroidアプリ担当
    ## 趣味・最近気になってること
    野球観戦、CTF、minifyされたJSから隠し機能を見つけること
    講演中に「OK Google」言ったらテロ起こせる説

    View Slide

  3. #DroidKaigi_room5
    # 本日の内容
    - アプリを「エミュレートする」アプリとは
    - マルチアカウントアプリの仕組み
    - マルチアカウントアプリは安全?危険?
    ※ マルチアカウントアプリ ≒ アプリをエミュレートするアプリ

    View Slide

  4. #DroidKaigi_room5
    # アプリを「エミュレートする」アプリとは
    - 一般には「マルチアカウントアプリ」として知られる
    - LINEやソシャゲなど、マルチアカウントの仕組みを持た
    ないアプリの別アカウントを作れることがウリ
    - 2016年ごろから急増
    マルチアカウントアプリの例
    Parallel Space GO Multiple 2Accounts

    View Slide

  5. #DroidKaigi_room5
    # マルチアカウントアプリの種類
    ## APKを改竄してインストールするタイプ
    - アプリケーションIDを変更して自己署名するタイプ
    - 一般に証明書のチェックで弾ける
    - App Clonerなどが有名
    ## 別のアプリとしてインストールせずに動かすタイプ
    - 「マルチアカウントアプリ」の中で動かすタイプ
    - あたかも正常にインストールされたかのように動かされ
    るため、動かされる側のアプリには判定が少し厳しい
    - 今回はこちらの話

    View Slide

  6. #DroidKaigi_room5
    最初に抱いた感想……
    これ、やばいアプリでは……

    View Slide

  7. #DroidKaigi_room5
    最初に抱いた感想……
    これ、やばいアプリでは……

    紐解いていきます

    View Slide

  8. #DroidKaigi_room5
    # 用語
    - マルチアカウントアプリ上で動くアプリのこと
    ➔ ゲストアプリ
    - マルチアカウントアプリのこと
    ➔ ホストアプリ

    View Slide

  9. #DroidKaigi_room5
    # 関連するものの本日深くは解説しないこと
    - Androidのシステムがどう動いているか
    - 普通のアプリがどうやって動いているか
    - APKファイルの構造
    - Linuxの超基礎知識

    View Slide

  10. #DroidKaigi_room5
    マルチアカウントアプリの仕組み

    View Slide

  11. #DroidKaigi_room5
    # VirtualAppの紹介
    - GitHub上でソースが公開されているマルチアカウントア
    プリ
    - VirtualAppは商用アプリのため、勝手にソースを組み
    込むことを禁止している
    https://github.com/asLody/VirtualApp/

    View Slide

  12. #DroidKaigi_room5
    # AndroidManifest.xmlから挙動を読み解く

    View Slide

  13. #DroidKaigi_room5
    # AndroidManifest.xmlから挙動を読み解く
    uses-permissionの記述 (186個)
    StubActivityを継承したActivity
    (100個)
    StubContentProviderを継承した
    ContentProvider (50個)

    View Slide

  14. #DroidKaigi_room5
    # AndroidManifest.xmlから挙動を読み解く
    ## AndroidManifestから読み解けること
    - パーミッションは考えうる全てのものをVirtualApp側
    に記載
    - システムの欠陥を突いて……とかではない
    - StubActivityは各アプリを動かしている……?

    View Slide

  15. #DroidKaigi_room5
    # AndroidManifest.xmlから挙動を読み解く
    ## AndroidManifestから読み解けること
    - パーミッションは考えうる全てのものをVirtualApp側
    に記載
    - システムの欠陥を突いて……とかではない
    - StubActivityは各アプリを動かしている……?
    ↑ ※違います!! (後で解説)

    View Slide

  16. #DroidKaigi_room5
    # ざっくりとした起動までの流れ
    0. 動かしたいアプリのAPKファイルを読み込みインストール
    1. ゲストアプリプロセスの起動
    2. Activityを起動

    View Slide

  17. #DroidKaigi_room5
    # ざっくりとした起動までの流れ
    0. 動かしたいアプリのAPKファイルを読み込みインストール
    1. ゲストアプリプロセスの起動
    2. Activityを起動

    View Slide

  18. #DroidKaigi_room5
    # アプリのインストール処理
    - android.content.pm.PackageParser※を使ってパッ
    ケージ情報を取得
    - /data/data/io.virtualapp/virtual/data以下
    に、各アプリのデータ領域にあたるディレクトリなどを
    作成
    - APKファイルからlib/**/*.soなどをコピー

    View Slide

  19. #DroidKaigi_room5
    # PackageParserについて
    - AndroidManifest.xmlをパースするクラス
    - JavaDocで@hide指定された隠蔽クラスのため、SDKに
    は含まれず通常使用ができない
    /**
    ...
    * @hide
    */
    public class PackageParser {
    ...

    View Slide

  20. #DroidKaigi_room5
    # 隠蔽クラス/メソッドの使用
    - @hide指定による隠蔽クラスやメソッドは、Javaのリフ
    レクションを用いるとアプリからもアクセス可能
    - ただしAndroidバージョンごとに大幅に仕様変更がある
    ので通常使用すべきではない
    - 解説するメソッドの多くは隠蔽メソッドなのでその度に
    言及は特にしない

    View Slide

  21. #DroidKaigi_room5
    # リフレクションとは
    - privateメソッドを実行する
    - privateなメンバ変数を取得・書換する
    - クラス名文字列から対応するクラスのインスタンスを生
    成する(コンストラクタに引数を渡す場合)
    - メソッド実行前後に処理を挟む、実行結果を変える
    (Proxy)
    などが可能なJavaの機能(いわゆる黒魔術)

    View Slide

  22. #DroidKaigi_room5
    # privateメンバ変数の取得
    Field f = activity.getClass()
    .getDeclaredField("mMainThread");
    f.setAccessible(true);
    ActivityThread mainThread =
    (ActivityThread) f.get(activity);

    View Slide

  23. #DroidKaigi_room5
    # 名前からインスタンス生成 (LayoutInflaterの処理)
    Class extends View> clazz = context.getClassLoader()
    .loadClass(name)
    .asSubclass(View.class);
    Constructor extends View> constructor =
    clazz.getConstructor(constructorSignature);
    constructor.setAccessible(true);
    View view = constructor.newInstance(args);

    View Slide

  24. #DroidKaigi_room5
    # Proxyの例
    Map proxyInstance = (Map) Proxy.newProxyInstance(
    SomeTest.class.getClassLoader(),
    new Class[] { Map.class },
    (proxy, method, args) -> {
    if (method.getName().equals("get")) return 42;
    throw new UnsupportOperationException();
    });
    proxyInstance.get(); // 42
    proxyInstance.put(1, 1); // error

    View Slide

  25. #DroidKaigi_room5
    # インストール後のdataディレクトリ
    /data/data/io.virtualapp/virtual/data内
    - app/[application id]/
    ∗ 各アプリのネイティブバイナリ・パッケージ
    キャッシュを保存
    - user/0/[application id]
    ∗ データディレクトリ
    (/data/data/[application id]) に相当

    View Slide

  26. #DroidKaigi_room5
    # ざっくりとした起動までの流れ
    0. 動かしたいアプリのAPKファイルを読み込みインストール
    1. ゲストアプリプロセスの起動
    2. Activityを起動

    View Slide

  27. #DroidKaigi_room5
    # プロセスの状態
    ホストアプリプロセス Managerプロセス
    ゲストアプリプロセス
    (ContentProvider)
    Activity起動要求
    ゲスト起動処理
    プロセス起動
    VMのロード
    & JNIをフック
    ActivityThread改竄
    (Application作成)

    View Slide

  28. #DroidKaigi_room5
    # プロセスの状態
    ホストアプリプロセス Managerプロセス
    ゲストアプリプロセス
    (ContentProvider)
    Activity起動要求
    ゲスト起動処理
    プロセス起動
    VMのロード
    & JNIをフック
    ActivityThread改竄
    (Application作成)

    View Slide

  29. #DroidKaigi_room5
    # ContentProviderとは
    コンテンツ プロバイダは、データの中央リポジトリへのア
    クセスを管理します。(中略)
    これは、プロバイダと他のアプリケーションとの間のイン
    ターフェースになります。
    https://developer.android.com/guide/topics/pr
    oviders/content-provider-creating.html?hl=ja
    - ContentUrisという仕組みでSQLiteのデータをやりと
    りしたりする
    - ContentProviderを含むプロセスはkillされにくい!

    View Slide

  30. #DroidKaigi_room5
    # StubContentProviderの起動
    - ContentProviderは別プロセスで動かせるため、この
    プロセスをアプリプロセスに見立てて動かす
    - ランタイムのセットアップに関する処理は
    StubContentProvider上で行う
    - ゲストアプリごとに別プロセスの
    StubContentProviderを起動する

    View Slide

  31. #DroidKaigi_room5
    # プロセスの状態
    ホストアプリプロセス Managerプロセス
    ゲストアプリプロセス
    (ContentProvider)
    Activity起動要求
    ゲスト起動処理
    プロセス起動
    VMのロード
    & JNIをフック
    ActivityThread改竄
    (Application作成)

    View Slide

  32. #DroidKaigi_room5
    # ART/DalvikVMをロードする
    - ネイティブ側でlibart.soもしくはlibdvm.soをロード
    (dlopen)する
    - dlopenにもパスを変更する処理が追加されている
    - JNIのフックについては後述

    View Slide

  33. #DroidKaigi_room5
    # ART/DalvikVMをロードする (補足)
    - システムプロパティに
    persist.sys.dalvik.vm.lib.2が存在
    → 通常はlibart.soのパスがセットされている
    persist.sys.dalvik.vm.libが存在
    → 通常はlibdvm.soのパスがセットされている

    View Slide

  34. #DroidKaigi_room5
    # プロセスの状態
    ホストアプリプロセス Managerプロセス
    ゲストアプリプロセス
    (ContentProvider)
    Activity起動要求
    ゲスト起動処理
    プロセス起動
    VMのロード
    & JNIをフック
    ActivityThread改竄
    (Application作成)

    View Slide

  35. #DroidKaigi_room5
    # ActivityThreadの略説
    - アプリケーションを動かしてる一番重要な部分
    - public static void main(String[] args)があるところ
    - ブレークポイントを貼るとコールスタックの初めの方に出てくる
    やつ
    - Looperが回ってるところ
    - HというHandlerを継承したクラスがあり、そこでライフ
    サイクルイベントやActivity起動などを処理している
    - 詳しくはAndroidを支える技術〈Ⅱ〉の5章を読んでね!

    View Slide

  36. #DroidKaigi_room5
    # ActivityThreadの改竄
    - ContentProviderの生成と同時にActivityThreadも
    生成される
    - ゲストアプリを動かす際、ActivityThread内で持つ情
    報をゲストのものに改竄する必要があるので改竄を行う
    - Proxyを使ったメソッドの処理改竄などもここで行う

    View Slide

  37. #DroidKaigi_room5
    # ActivityThreadの改竄
    # ActivityThread#mBoundApplication (AppBindData) の改竄
    - ActivityThreadから取得し、情報を改竄
    - appInfo, providers → PackageParserで取得
    - processName → パッケージ名
    - instrumentationName → パッケージ名などから生成

    View Slide

  38. #DroidKaigi_room5
    # ActivityThreadの改竄
    # AppBindData#info (LoadedApk) の生成
    - Context#createPackageContext(packageName)を
    用いて、対象のアプリのContextを取得
    - ContextImpl#mPackageInfo (LoadedApk) を取得

    View Slide

  39. #DroidKaigi_room5
    # ActivityThreadの改竄
    # Applicationの生成
    - 生成したLoadedApkのインスタンスを用いて
    LoadedApk#makeApplication()を実行し生成
    - ActivityThread#mInitialApplicationに作成した
    Applicationをセット
    - 生成後ライフサイクル処理を走らせる

    View Slide

  40. #DroidKaigi_room5
    # ざっくりとした起動までの流れ
    0. 動かしたいアプリのAPKファイルを読み込み、インストー

    1. ゲストアプリプロセスの起動
    2. Activityを起動

    View Slide

  41. #DroidKaigi_room5
    # プロセスの状態
    ホストアプリプロセス Managerプロセス
    ゲストアプリプロセス
    (ContentProvider)
    Activity起動要求
    ゲスト起動処理
    プロセス起動
    Context#
    startActivity
    Intent差し替え
    Intentから
    Activityを生成

    View Slide

  42. #DroidKaigi_room5
    # プロセスの状態
    ホストアプリプロセス Managerプロセス
    ゲストアプリプロセス
    (ContentProvider)
    Activity起動要求
    ゲスト起動処理
    プロセス起動
    Context#
    startActivity
    Intent差し替え
    Intentから
    Activityを生成

    View Slide

  43. #DroidKaigi_room5
    # Activityの起動
    - ManagerプロセスからstartActivity(intent)で
    StubActivityを起動しようとする
    - StubActivityとStubContentProviderは同じプロセスで動
    くようAndroidManifestで定義されている
    - Androidの機能でゲストプロセス側にIntentが飛ぶ

    View Slide

  44. #DroidKaigi_room5
    # プロセスの状態
    ホストアプリプロセス Managerプロセス
    ゲストアプリプロセス
    (ContentProvider)
    Activity起動要求
    ゲスト起動処理
    プロセス起動
    Context#
    startActivity
    Intent差し替え
    Intentから
    Activityを生成
    Intent
    Target: io.virtualapp/.StubActivity
    Extra: Intent(x.guest/.MainActivity)

    View Slide

  45. #DroidKaigi_room5
    # プロセスの状態
    ホストアプリプロセス Managerプロセス
    ゲストアプリプロセス
    (ContentProvider)
    Activity起動要求
    ゲスト起動処理
    プロセス起動
    Context#
    startActivity
    Intent差し替え
    Intentから
    Activityを生成

    View Slide

  46. #DroidKaigi_room5
    # ActivityThreadの略説
    - アプリケーションを動かしてる一番重要な部分
    - public static void main(String[] args)があるところ
    - ブレークポイントを貼るとコールスタックの初めの方に出てくる
    やつ
    - Looperが回ってるところ
    - HというHandlerを継承したクラスがあり、そこでライフ
    サイクルイベントやActivity起動などを処理している
    - 詳しくはAndroidを支える技術〈Ⅱ〉の5章を読んでね!

    View Slide

  47. #DroidKaigi_room5
    # Activityの起動
    - ゲストアプリプロセスでIntentを受信後、
    H#dispatchMessage(msg)が呼ばれる
    - 通常は受信したIntentからActivity生成処理が走る

    View Slide

  48. #DroidKaigi_room5
    Handlerクラスの実装
    public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
    handleCallback(msg);
    } else {
    if (mCallback != null
    && mCallback.handleMessage(msg)) {
    return;
    }
    handleMessage(msg);
    }
    }

    View Slide

  49. #DroidKaigi_room5
    Handlerクラスの実装
    public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
    handleCallback(msg);
    } else {
    if (mCallback != null
    && mCallback.handleMessage(msg)) {
    return;
    }
    handleMessage(msg);
    }
    }
    mCallbackがセットされていて、
    実行の結果trueが返ってきたら終了

    View Slide

  50. #DroidKaigi_room5
    Handlerクラスの実装
    public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
    handleCallback(msg);
    } else {
    if (mCallback != null
    && mCallback.handleMessage(msg)) {
    return;
    }
    handleMessage(msg);
    }
    }
    Handlerのコンストラクタに渡す
    コールバックで、Hではnull
    Hでは通常ここの処理でActivity生成

    View Slide

  51. #DroidKaigi_room5
    # プロセスの状態
    ホストアプリプロセス Managerプロセス
    ゲストアプリプロセス
    (ContentProvider)
    Activity起動要求
    ゲスト起動処理
    プロセス起動
    VMのロード
    & JNIをフック
    ActivityThread改竄
    (Application作成)
    ゲストプロセスの
    ActivityThread#mH#mCallbackに
    処理を追加
    Context#
    startActivity

    View Slide

  52. #DroidKaigi_room5
    # Activityの起動
    - ゲストアプリプロセスでIntentを受信後、
    H#dispatchMessage(msg)が呼ばれる
    - この中で通常は受信したIntentからActivity生成処理が走る
    - H#mCallbackにセットした処理により、Intentの宛先
    がゲストアプリの起動したいActivityに置き換わる
    - このIntentを基にActivityを生成し起動する

    View Slide

  53. #DroidKaigi_room5
    # プロセスの状態
    ホストアプリプロセス Managerプロセス
    ゲストアプリプロセス
    (ContentProvider)
    Activity起動要求
    ゲスト起動処理
    プロセス起動
    Context#
    startActivity
    Intent差し替え
    Intentから
    Activityを生成
    Intent
    Target: io.virtualapp/.StubActivity
    Extra: Intent(x.guest/.MainActivity)

    View Slide

  54. #DroidKaigi_room5
    # プロセスの状態
    ホストアプリプロセス Managerプロセス
    ゲストアプリプロセス
    (ContentProvider)
    Activity起動要求
    ゲスト起動処理
    プロセス起動
    Context#
    startActivity
    Intent差し替え
    Intentから
    Activityを生成
    Intent
    Target: x.guest/.MainActivity

    View Slide

  55. #DroidKaigi_room5
    # なぜStubActivityを経由?
    - 別アプリから起動する場合を考慮した結果

    View Slide

  56. #DroidKaigi_room5
    # 別アプリから起動する場合
    別アプリ
    ゲストアプリプロセス
    (ContentProvider)
    startActivity
    ContentProvider
    起動
    mCallbackで
    Intent差し替え
    Intentから
    Activityを生成
    StubActivity起動
    Intent
    Target: io.virtualapp/.StubActivity
    Extra: Intent(x.guest/.MainActivity)

    View Slide

  57. #DroidKaigi_room5
    # AndroidManifest.xmlの答え合わせ
    StubActivity (100個)
    StubContentProvider (50個)
    Activityの起動を仲介する
    ダミーのActivityたち
    ゲストアプリの
    プロセスになる奴ら

    View Slide

  58. #DroidKaigi_room5
    # 補足:メソッドのフック
    # なぜ必要?
    - Intentの処理やCameraのインストールなどがパッケー
    ジ名に依存している
    - PackageCacheのパスを変えないとPermission
    Deniedになる
    - Binder#getCallingUid()の返すUidを偽装
    - その他パッケージ改竄検出をすり抜けるため

    View Slide

  59. #DroidKaigi_room5
    # Javaのリフレクション (Proxy) で改竄する処理
    - startActivityなどIntent処理
    - 改ざんしないと本物が起動してしまうため
    - バックアップなど多くのメソッドの挙動を削除
    - 動作しては困るものは削除してしまう
    - オーディオ周りなどの挙動をホストの処理に置き換え
    - ネイティブ周りなどの関係でそのままでは動かないので
    などなど

    View Slide

  60. #DroidKaigi_room5
    # JNIのメソッドをフックして行うこと
    - UIDを偽装
    - ゲストのdataディレクトリを偽装
    - Cameraの初期化
    - AudioRecordのパーミッションを取得
    - PackageCacheに使うパスを変更
    詳しくはスライド最後の付録で

    View Slide

  61. #DroidKaigi_room5
    マルチアカウントアプリの危険性

    View Slide

  62. #DroidKaigi_room5
    # 広告をオーバーライドされるなどの問題
    - 広告フレームワークの処理を変え、マルチアカウントア
    プリ作者に収益が入るようにすることも技術的には可能
    - Parallel Space上のAdMobでは変更されていないことを確認
    したが、他のアプリはどうかわからない
    - その他、Playストアでの課金をしたかのように振る舞わ
    せるなども可能と考えられる

    View Slide

  63. #DroidKaigi_room5
    # 過剰にパーミッションを与えてしまう危険性
    - マルチアカウントアプリに非常に多くのパーミッション
    が使われているため、ゲストアプリにも同様のパーミッ
    ションを与えてしまう危険性がある
    - ゲストアプリに悪意がある場合、AndroidManifestで
    はパーミッションを指定せず、マルチアカウントアプリ
    上で動く時のみパーミッションを用いる、などが可能
    - そもそもマルチアカウントアプリ自体が大変危険なアプ
    リかもしれない

    View Slide

  64. #DroidKaigi_room5
    # アカウントデータ・パスワード漏洩の危険性
    - マルチアカウントアプリに悪意がある場合、容易にパス
    ワード入力情報から内部のデータまで全て盗むことが可

    - ゲストアプリが悪意のあるものの場合も、他のゲストア
    プリの保存データを取得可能
    ↓ VirtualApp上の端末エミュレータアプリでLINEの情報を確認できた

    View Slide

  65. #DroidKaigi_room5
    # 応用すると……
    - 知らぬ間にアプリが置き換わっていることがあるかも……
    - いろいろ頑張ればユーザーが気付かないようにアプリをアンイン
    ストールさせることが可能
    - 代わりに悪意のあるアプリケーション上で動かすショートカット
    を作っておけば、ユーザーは置き換わっていることに気付くのは
    困難
    ⇒ 対策した方がよいかも……

    View Slide

  66. #DroidKaigi_room5
    # マルチアカウントアプリの対策
    - 単純な証明書のチェックはNG
    - マルチアカウントアプリ上では証明書を取得するメソッドも書き
    換わっており、正しい証明書を得てしまう
    - パッケージ名やUIDのチェック等もNG
    - これも完全にホストのものに差し替えられると確認が困難

    View Slide

  67. #DroidKaigi_room5
    # 2018年現在最適なチェック方法!!
    - psコマンドで確認(一部省略)
    $ ps
    USER PID NAME
    u0_a51 8561 ps
    u0_a51 29616 com.lbe.parallel.intl:mdserver
    u0_a51 29699 parallel.monitor
    u0_a51 30703 jackpal.androidterm
    u0_a51 30889 /system/bin/sh
    自分のアプリでは発生し得ないプロセスが同じユーザーとして実行され
    ていることをチェックできる

    View Slide

  68. #DroidKaigi_room5
    # 最後に(対策する前に)
    - マルチアカウントアプリが使われるアプリ
    ≒ ユーザーはアカウント切り替え機能を求めている
    ⇒ マルチアカウント機能の実装を検討すべきでは?
    - 現在マルチアカウントアプリの紹介記事の中で危険性を
    伝える記事はないので、正しく周知していくことが必要
    - 必ずしも危険なものではないが、危険性は知るべき

    View Slide

  69. #DroidKaigi_room5
    付録:JNIのフック処理の詳細
    参考(中国語):https://www.jianshu.com/p/052b6dd45659

    View Slide

  70. #DroidKaigi_room5
    # JNIの基礎知識
    - JNIからJavaのメソッドを呼ぶ機能がある
    // C++
    jclass clazz =
    env->FindClass("SomeJavaClass");
    jmethodID methodId =
    env->GetStaticMethodID(clazz, "javaMethod", "()V");
    env->CallStaticVoidMethod(clazz, methodId);

    View Slide

  71. #DroidKaigi_room5
    # JNIの基礎知識
    - JNIからJavaのメソッドを呼ぶ機能がある
    // C++
    jclass clazz =
    env->FindClass("SomeJavaClass");
    jmethodID methodId =
    env->GetStaticMethodID(clazz, "javaMethod", "()V");
    env->CallStaticVoidMethod(clazz, methodId);

    View Slide

  72. #DroidKaigi_room5
    # JNIのフック
    jmethodIDはMethod構造体のポインタ
    // C++
    struct Method {
    ClassObject* clazz; // クラスを表す
    u4 accessFlags; // publicか、nativeか、などのフラグ

    DalvikBridgeFunc nativeFunc; // ネイティブ関数のポインタ

    };

    View Slide

  73. #DroidKaigi_room5
    # JNIのフック
    jmethodIDはMethod構造体のポインタ
    // C++
    struct Method {
    ClassObject* clazz; // クラスを表す
    u4 accessFlags; // publicか、nativeか、などのフラグ

    DalvikBridgeFunc nativeFunc; // ネイティブ関数のポインタ

    }; これを書き換えればOK

    View Slide

  74. #DroidKaigi_room5
    # JNIのフック
    jmethodIDはMethod構造体のポインタ
    // C++
    struct Method {
    ClassObject* clazz; // クラスを表す
    u4 accessFlags; // publicか、nativeか、などのフラグ

    DalvikBridgeFunc nativeFunc; // ネイティブ関数のポインタ

    };
    環境によって構造が異なる

    View Slide

  75. #DroidKaigi_room5
    # JNIのフック
    jmethodIDはMethod構造体のポインタ
    // C++
    struct Method {
    ClassObject* clazz; // クラスを表す
    u4 accessFlags; // publicか、nativeか、などのフラグ

    DalvikBridgeFunc nativeFunc; // ネイティブ関数のポインタ

    };
    環境によって構造が異なる
    ここのオフセットを知
    る必要あり

    View Slide

  76. #DroidKaigi_room5
    # JNIのフック
    ダミーのメソッドをJava, C++両側に定義
    // Java
    static native void nativeMark();
    // C++
    void mark() {}

    View Slide

  77. #DroidKaigi_room5
    # JNIのフック
    ダミーメソッドのポインタに一致するオフセットを探索
    // C++
    size_t start = (size_t) env->GetStaticMethodID(
    clazz, "nativeMark", "()V");
    size_t target = (size_t) mark;
    int offset = 0;
    while (true) {
    if (*((size_t *)(start + offset)) == target)
    break;
    offset += 4;
    }

    View Slide

  78. #DroidKaigi_room5
    # JNIのフック
    このオフセットを使って他のメソッドを置き換える
    // C++
    size_t method = (size_t) someMethodId;
    void **nativeFuncPtr = (void **) (method + offset);
    *nativeFuncPtr = (void*) hookFunc;

    View Slide