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

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

アプリをエミュレートするアプリの登場とその危険性 / 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 # 自己紹介 ## 名前 星川貴樹 Takaki Hoshikawa ## ニックネーム

    @oboenikui ## 会社 エムスリー株式会社(新卒1年目) 主にAndroidアプリ担当 ## 趣味・最近気になってること 野球観戦、CTF、minifyされたJSから隠し機能を見つけること 講演中に「OK Google」言ったらテロ起こせる説
  2. #DroidKaigi_room5 # マルチアカウントアプリの種類 ## APKを改竄してインストールするタイプ - アプリケーションIDを変更して自己署名するタイプ - 一般に証明書のチェックで弾ける -

    App Clonerなどが有名 ## 別のアプリとしてインストールせずに動かすタイプ - 「マルチアカウントアプリ」の中で動かすタイプ - あたかも正常にインストールされたかのように動かされ るため、動かされる側のアプリには判定が少し厳しい - 今回はこちらの話
  3. #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);
  4. #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
  5. #DroidKaigi_room5 # ActivityThreadの略説 - アプリケーションを動かしてる一番重要な部分 - public static void main(String[]

    args)があるところ - ブレークポイントを貼るとコールスタックの初めの方に出てくる やつ - Looperが回ってるところ - HというHandlerを継承したクラスがあり、そこでライフ サイクルイベントやActivity起動などを処理している - 詳しくはAndroidを支える技術〈Ⅱ〉の5章を読んでね!
  6. #DroidKaigi_room5 # ActivityThreadの改竄 # ActivityThread#mBoundApplication (AppBindData) の改竄 - ActivityThreadから取得し、情報を改竄 -

    appInfo, providers → PackageParserで取得 - processName → パッケージ名 - instrumentationName → パッケージ名などから生成
  7. #DroidKaigi_room5 # プロセスの状態 ホストアプリプロセス Managerプロセス ゲストアプリプロセス (ContentProvider) Activity起動要求 ゲスト起動処理 プロセス起動

    Context# startActivity Intent差し替え Intentから Activityを生成 Intent Target: io.virtualapp/.StubActivity Extra: Intent(x.guest/.MainActivity)
  8. #DroidKaigi_room5 # ActivityThreadの略説 - アプリケーションを動かしてる一番重要な部分 - public static void main(String[]

    args)があるところ - ブレークポイントを貼るとコールスタックの初めの方に出てくる やつ - Looperが回ってるところ - HというHandlerを継承したクラスがあり、そこでライフ サイクルイベントやActivity起動などを処理している - 詳しくはAndroidを支える技術〈Ⅱ〉の5章を読んでね!
  9. #DroidKaigi_room5 Handlerクラスの実装 public void dispatchMessage(Message msg) { if (msg.callback !=

    null) { handleCallback(msg); } else { if (mCallback != null && mCallback.handleMessage(msg)) { return; } handleMessage(msg); } }
  10. #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が返ってきたら終了
  11. #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生成
  12. #DroidKaigi_room5 # プロセスの状態 ホストアプリプロセス Managerプロセス ゲストアプリプロセス (ContentProvider) Activity起動要求 ゲスト起動処理 プロセス起動

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

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

    Intent差し替え Intentから Activityを生成 StubActivity起動 Intent Target: io.virtualapp/.StubActivity Extra: Intent(x.guest/.MainActivity)
  15. #DroidKaigi_room5 # Javaのリフレクション (Proxy) で改竄する処理 - startActivityなどIntent処理 - 改ざんしないと本物が起動してしまうため -

    バックアップなど多くのメソッドの挙動を削除 - 動作しては困るものは削除してしまう - オーディオ周りなどの挙動をホストの処理に置き換え - ネイティブ周りなどの関係でそのままでは動かないので などなど
  16. #DroidKaigi_room5 # JNIのメソッドをフックして行うこと - UIDを偽装 - ゲストのdataディレクトリを偽装 - Cameraの初期化 -

    AudioRecordのパーミッションを取得 - PackageCacheに使うパスを変更 詳しくはスライド最後の付録で
  17. #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 自分のアプリでは発生し得ないプロセスが同じユーザーとして実行され ていることをチェックできる
  18. #DroidKaigi_room5 # 最後に(対策する前に) - マルチアカウントアプリが使われるアプリ ≒ ユーザーはアカウント切り替え機能を求めている ⇒ マルチアカウント機能の実装を検討すべきでは? -

    現在マルチアカウントアプリの紹介記事の中で危険性を 伝える記事はないので、正しく周知していくことが必要 - 必ずしも危険なものではないが、危険性は知るべき
  19. #DroidKaigi_room5 # JNIの基礎知識 - JNIからJavaのメソッドを呼ぶ機能がある // C++ jclass clazz =

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

    env->FindClass("SomeJavaClass"); jmethodID methodId = env->GetStaticMethodID(clazz, "javaMethod", "()V"); env->CallStaticVoidMethod(clazz, methodId);
  21. #DroidKaigi_room5 # JNIのフック jmethodIDはMethod構造体のポインタ // C++ struct Method { ClassObject*

    clazz; // クラスを表す u4 accessFlags; // publicか、nativeか、などのフラグ … DalvikBridgeFunc nativeFunc; // ネイティブ関数のポインタ … };
  22. #DroidKaigi_room5 # JNIのフック jmethodIDはMethod構造体のポインタ // C++ struct Method { ClassObject*

    clazz; // クラスを表す u4 accessFlags; // publicか、nativeか、などのフラグ … DalvikBridgeFunc nativeFunc; // ネイティブ関数のポインタ … }; これを書き換えればOK
  23. #DroidKaigi_room5 # JNIのフック jmethodIDはMethod構造体のポインタ // C++ struct Method { ClassObject*

    clazz; // クラスを表す u4 accessFlags; // publicか、nativeか、などのフラグ … DalvikBridgeFunc nativeFunc; // ネイティブ関数のポインタ … }; 環境によって構造が異なる
  24. #DroidKaigi_room5 # JNIのフック jmethodIDはMethod構造体のポインタ // C++ struct Method { ClassObject*

    clazz; // クラスを表す u4 accessFlags; // publicか、nativeか、などのフラグ … DalvikBridgeFunc nativeFunc; // ネイティブ関数のポインタ … }; 環境によって構造が異なる ここのオフセットを知 る必要あり
  25. #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; }
  26. #DroidKaigi_room5 # JNIのフック このオフセットを使って他のメソッドを置き換える // C++ size_t method = (size_t)

    someMethodId; void **nativeFuncPtr = (void **) (method + offset); *nativeFuncPtr = (void*) hookFunc;