20201208のAndroidに関する記事は5件です。

【Android】root判定によるチート対策

PONOS Advent Calendar 2020 9日目の記事です。

昨日は@nissy_gpさんのNode.js+Sequelizeで楽観的ロックを使って動作を確認するでした。

はじめに:eyeglasses:

前回の記事で「Android開発者向けオプションを使ったチート対策」を紹介しました。
今回は、「root判定によるチート対策」を紹介します。

この記事の対象者:eyeglasses:

・アプリ開発者
・Android開発者

root化された端末の判定

メモリハックによる値書き換えなどのチート行為は
root化された端末を使用して行われるケースが多いです。

今回はアプリ側からsuコマンドを実行して
IOExceptionの有無で判定してみましょう。
IOException発生の場合はroot権限なし ー> 通常端末
IOException発生しない場合はroot権限あり ー> root端末

java.lang.Runtimeクラスの
exec(String command)メソッドを使用します。

boolean isRoot = false;

try {
    Process process = Runtime.getRuntime().exec("su");
    process.destroy();
    isRoot = true;
    Log.d(TAG, "Root!!");
} catch (IOException e) {
    Log.d(TAG, "NotRoot!!");
} catch (Exception e){
    e.printStackTrace();
}

if(isRoot){
    //root権限ありの時の処理
    //例:情報をサーバーに送信し、root化解除を促すポップアップ表示
}

メリット
アプリのセキュリティーレベルを上げることができる。
実装コストが軽い

デメリット
マニアックな海外製端末で初期出荷状態から
root権限が付与されているケースなどがあり、健全ユーザーがチート扱いされてしまう場合もある。
※経験則になってしまいますが、このパターンはかなりレアケースでした。

注意点
リバースエンジニアリングによるコード改竄で今回のコードを無効化されてしまう恐れもあります。
root対策と同時にリパッケージされた不正アプリ対策を実装するのがお勧めです。
不正アプリ対策
・メモリシャッフル
・難読化
・SafetyNet導入
などがあり、SafetyNet の Attestation APIを使用してリパッケージされていないか判定することができます。

SafetyNetの紹介

SafetyNetでできること
SafetyNet Attestation API
正規の Android デバイスで、正規のアプリかどうか判定できる

SafetyNet Safe Browsing API
URL が Google によって既知の脅威としてマークされているかどうかを判定できる

SafetyNet reCAPTCHA API
悪意のあるトラフィックを検知することができる

SafetyNet Verify Apps API
有害なアプリから端末を保護することができる

SafetyNet Attestation API

実装方法は公式に記載されているので省略しますが
以下の手順で正規アプリかどうか判定することができます。
1.SafetyNet 構成証明をリクエスト

2.SafetyNet 構成認証レスポンスを各種検証

3.apkPackageName: 呼び出し元のアプリのパッケージ名のチェック

4.APK証明書のSHA-256ハッシュ:署名に使用したAPK証明書を検証

5.APKのSHA-256ハッシュ:APKがリリースしたものから改ざんされていないことを確認するために、APKのSHA-256ハッシュが同一か検証

6.BasicIntegrityがtrueであることを確認

※検証処理はサーバー側で行うこと

まとめ

今回紹介したroot判定は自前で実装する手法でしたが
SafetyNet Attestation API
を使用してでも判定することができます。
構成認証レスポンスのBasicIntegrityがfalseの場合はroot端末です

他にもSafetyNetを使用したチート対策があるので
公式ドキュメントを読んでおくことをお勧めします。

明日は@blockの記事です。
お楽しみに!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DroidScriptを(windows10)NoxPlayerでインストールして、自作javascriptを実行してみた。

大丈夫ですか?
(参考)NoxPlayerに危険性はある?利用時に考えられる3つの可能性
https://mayonez.jp/topic/1088522

環境:

windows10
NoxPlayer バージョン7.0.0.7 2020/11/27
DroidScript バージョン1.80(Android 要件4.1 以上) 更新日2020年1月20日

自作javascript:

DroidScriptで、3行3列のボタンのレイアウトをループや配列を使って、短くなりますか?
https://ja.stackoverflow.com/questions/72486/droidscript%e3%81%a7-3%e8%a1%8c3%e5%88%97%e3%81%ae%e3%83%9c%e3%82%bf%e3%83%b3%e3%81%ae%e3%83%ac%e3%82%a4%e3%82%a2%e3%82%a6%e3%83%88%e3%82%92%e3%83%ab%e3%83%bc%e3%83%97%e3%82%84%e9%85%8d%e5%88%97%e3%82%92%e4%bd%bf%e3%81%a3%e3%81%a6-%e7%9f%ad%e3%81%8f%e3%81%aa%e3%82%8a%e3%81%be%e3%81%99%e3%81%8b?noredirect=1&lq=1

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Android】DaggerなんもわからんかったけどHiltをやりました

この記事はand factory Advent Calendar 2020の9日目の記事です。
昨日は@hagmonさんのエンジニアリングマネージャーについて考えるでした。
メンバーの自走のサポートを中心に考えるマネジメントで、上位者、下位者ともに能力を発揮できる良い組織になりそうですね??(自社自賛)

はじめに

お世話になっております。
1年くらい前にDaggerを導入しようとして「なんもわからん・・・」となり、
一時Koinに甘えたりしていた者です。

近頃、Dagger-Hilt、並びに同ライブラリのAndroidStudio上でのサポートなどが新たに登場し、情勢の変化が起きております。
これらの事情を踏まえ、弊開発環境でも今一度Google社製DIライブラリとの親善に向き合うため、アドベントカレンダーの舞台を機会として筆を執っております。

という感じでこういう記事です。↓

  • はなすこと
  • はなさないこと
    • 「DIってなんやねん」系のアレ
    • Koinについて
    • 別ライブラリからの移行とかの話
      (Koinからの移行は供給が少なそうな気がするので、そのうちやったら別で書いてみたい)

「わからんかった」人間の記事なので、ツッコミどころがあればなんなりとマサカリを頂けると助かります。本当に。

導入

ほとんど思考停止でやっておく部分です。

project_root/build.gradle
    // きっと他にも書いてある
    ext.hilt_version = '2.28-alpha'
    dependencies {
        // きっと他にも書いてある
        classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
    }

バージョンは良い感じのを選んでくれよな!?(まだalphaしかないんすねって思いました)

project_root/module/build.gradle
// きっと他にも書いてある
apply plugin: 'kotlin-kapt'
apply plugin: 'dagger.hilt.android.plugin'

// きっと android {...} とかが書いてある

dependencies {
    // きっと他にも書いてある
    implementation "com.google.dagger:hilt-android:$hilt_version"
    kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
}

ここまで外部依存関係系。

MyApplication.kt
@HiltAndroidApp
class MyApplication : Application() {
    // きっと他にも書いてある
}

で、Applicationクラスへのアノテーションで、アプリ全体へのコンテナ(Component)を生成してくれる様子。


基本的な使い方

「Fragmentで使うクラス、生成の処理書くんメンドいなあ」
「Hiltくん経由で生成して注入できるで」

MainActivity.kt
@AndroidEntryPoint
class LogsFragment : Fragment() {

    @Inject lateinit var dateFormatter: DateFormatter

    // 他の記述
}

Androidのクラス(ちゃんとした対象一覧はここ)@AndroidEntryPointアノテーションを付けると、そのクラスのライフサイクルに従うDIコンテナを生成してくれます。(このあたり、Daggerからかなり楽になった所っぽいです)
その上で、@Injectでフィールドインジェクションが可能です。
注意として、@Injectの対象はprivateにすることが出来ません。

「そんで、注入したいクラスの生成方法もちゃんと書いといてな」

DateFormatter.kt
class DateFormatter @Inject constructor() { 
    // 他の記述
}

コンストラクタに@InjectをつけることでHiltがクラスの生成方法を理解します。
コンストラクタにパラメータがある場合は、パラメータのクラスのコンストラクタを...と続けて依存グラフを生成します。(このあたりはDI自体の概念として基本)

とりあえずこれで一番シンプルな利用ができます。

インスタンスの共有

「このInjectいうやつ、毎回新しいインスタンスが生成されるん?」
「せやで」
「シングルトンとかで扱いたいんやけど」

DateFormatter.kt
@Singleton
class DateFormatter @Inject constructor() { 
    // 他の記述
}

いわゆるスコープの件です。スコープも、生成対象にアノテーションを付けて表現します。
他にも@ActivityScoped@FragmentScopedなどがあります。一覧はここ。

ちなみに、@Singletonは、実際のところ「Applicationレベルのスコープ」と言えます。

コンストラクタ以外を利用した注入

「ファクトリメソッドとか通さないといけないフィールドだと、コンストラクタ呼べなくて登録できひん・・・」
「Moduleいうのを使うんよ」

DatabaseModule.kt
@InstallIn(ApplicationComponent::class)
@Module
object DatabaseModule {

    @Provides
    fun provideLogDao(database: AppDatabase): LogDao {
        return database.logDao()
    }
}

@Moduleをコンテナとして、その中に関数のイン・アウトとして生成方法を示します。
このときの関数に、@Providesアノテーションを使用します。

Interfaceに対する実装を利用した注入

「フィールドの型にはインターフェースを指定して、インスタンスはその実装を使う時はクラスが違うやん。どうすんの?」
「それもModuleやけど、専用のアノテーションがあるね」

NavigationModule.kt
@InstallIn(ActivityComponent::class)
@Module
abstract class NavigationModule {

    @Binds
    abstract fun bindNavigator(impl: AppNavigatorImpl): AppNavigator
}

@Moduleを付与した抽象クラスの中で、抽象関数として入力:実装/出力:インターフェースの定義をします。
このときに@Bindアノテーションを使用します。
@Providesと似ていますが、@Bindは抽象関数にしか使えません。よって、親のModuleクラスも抽象クラスである必要があります。(同一のModuleに両方定義できません。)

@Providesで入力:実装/出力:インターフェースの関数を用意して、入力をそのままreturnする関数を実装しても同じことになるかと思います。

そのほか

Componentクラスを作らなくて良い

@AndroidEntryPointアノテーションを付けると、そのクラスのライフサイクルに従うDIコンテナを生成してくれます

と記載しましたが、Androidで使いそうなスコープごとのComponentは標準で用意されています。
それぞれ、どのコンテナに属するかを記載すれば利用できるようになります。
(ActivityやFragmentは@AndroidEntryPointにより自動、Moduleはしれっと書いていた@InstallInアノテーションにより「どのコンテナに属するか」を示します)

Qualifierをクラスで分けることができる

同じクラスを指定しつつ、別のインスタンスを利用したい場合の話です。
Daggerでは文字列で名称を指定して別のインスタンスであることを示していましたが、
Hiltではアノテーションクラスを定義して、それを付与することで区別します。
こっちのが型安全的で好きですね。

参考

Google Codelabs android-hilt
https://codelabs.developers.google.com/codelabs/android-hilt

あとがき

正直に言いまして、ここ2日くらいで初めてHiltについて学びました。

そして手を付けてみたら具体的なプロジェクトに使えていないこともあり「え、簡単では・・・?」となり、
「逆にこれDaggerとの比較の観点が無いと何が嬉しいのか説明できねえやつだわ」と困ったりしていました。

結果、少しDagger側の話も学び直したりもしまして、もしかしたら今なら1年前よりもMaster of Daggerがすんなり読めるのでは・・・?とか思っています。勢いで参加に名乗りを上げたアドカレでしたが、またひとつ勉強の機会になったと思います。ありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Kotlin】SharedFlowとStateFlowを読み解く

前置き

ACCESS Advent Calendar 2020 の9日目です。
初学者向けKotlin Coroutines Flowの続きで、SharedFlowとStateFlowに関する記事です。

Flowの復習

Flowはこういうやつでしたね。

View    |  ViewModel    | UseCase〜深層
-----------------------------------------------
        |               |
イベント  →  Coroutine起動 → 時間のかかる処理(非同期)
     |          |       ↓↓↓
描画  ←←←  出力データ加工 ←←← 出力データ送信(複数回)
        |               |
CounterUseCase.kt
suspend fun countStream(): Flow<Int> = flow {
    repeat(100) { count ->
        delay(100)
        emit(count) // 0 1 2 3 4 ... 99
    }
}

※View/ViewModelのコードは省略。こちらに載ってます。

Flowでの値の更新は、上記UseCaseのflow { ... }ラムダ式中でしかできません。
つまりViewModel側では値を更新できず、また.valueみたいに値の参照もできません
subscribeしてる数だけflow { ... }ラムダ式が呼ばれてしまうのも特徴です。

それだと状態保持とか処理リソースの節約には向いてないってことで、ホットストリームなFlowとして登場したのが、今回紹介するSharedFlowStateFlowです。

SharedFlowとは

複数箇所でのsubscribeでデータや状態を共有できるFlowで、処理リソースの節約に向いてます。
ただのFlowと違う点は以下。

sharedFlow.onEach {
    println("1")
}.launchIn(scope)

sharedFlow.onEach {
    println("2")
}.launchIn(scope)
  • こんな感じで複数subscribeしててもflow { ... }ラムダ式側は1回しか呼ばれない
  • 処理開始タイミングを選択できる(後述
  • ちゃんと止めるまでsubscribeされ続ける
    • このサンプルLatestNewsActivityのとこ)みたいにLiveDataに変換し、observe引数にLifecycleOwnerを設定すれば、表示中だけのsubscribeは可能
  • 色々高機能
    • replay: subscribeした瞬間、過去のn回の値を受信する
    • buffer: 複数subscribeかつ処理に時間がかかるとき、1回目に行われた処理をバッファリングして、2回目以降を早くしてくれる(一言では説明しにくい…こちらがわかりやすい)

StateFlowとは

状態保持に特化したSharedFlowです。LiveDataに似ています、というか実質の後継機能です。

  • 初期値が必須
  • 現在の状態を.valueで受け取れる
  • MutableStateFlowを使えば、.valueへの代入も可能
    • その際Coroutines Scopeは不要
  • launchInで直近の値を1つ受信する
  • 同じ値の代入は受信しない
  • waitとかを挟まず連続して値が変更されたとき、最後の値しか受信しない
    • つまり「状態」が「保持」されないと「状態変化」とみなされない

LiveDataとの違いは、こちらの記事がわかりやすいです。

初期化

MutableSharedFlowlink)、MutableStateFlowlink)を使い、このように初期化します。

Flowからの変換

shareInlink)、stateInlink)を使い、このように変換します。

処理開始タイミングの指定

flow { ... }ラムダ式の処理開始タイミングは、shareInSharingStartedオプションで、変換時すぐ開始するか、subscribeが行われてから開始するかを選択できます。

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Xamarin.Android で Unity as a Library (うまくいった話)

この記事は、Xamarin Advent Calendar 2020の8日目の記事です。

はじめに

Xamarin.Android で Unity as a Library (をやろうとしてうまくいかなった話) の記事を読んで、できないことはないよなと思い、自分でも試してみることにしました。

結果

記事の手順でできました。

試した環境は以下の通りです。

  • macOS Big Sur
  • Unity 2019.4.16f1
  • Android Studio 4.1.1
  • Visual Studio for Mac 8.8.3

実機(UMIDIGI A7 Pro)で試しています。また、デバッグ実行だとUnityの画面が表示されなかったのですが、デバッグなし実行だとうまくいきました。

Screenshot_20201208-001920.png

コード

こちら
記事にあったコードを拝借させていただきました。

おわりに

どうしてうまくいかなかったのかは定かではありませんが、ひとまず、Xamarin.Androidでも、Unity as a Libraryは使えそうです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む