Kotlinプロジェクトでどこまでキーを隠蔽化できるか（その4：C++での文字列難読化）

Kotlinを使ってAndroidアプリを作った際に、NCMBのアプリケーションキーとクライアントキーをどこまで隠蔽化できるのか試しています。前回は[C++を使ってコンパイル、隠蔽化する方法を試しました](https://qiita.com/goofmint/items/09c78dcf1fd664b85a37)。そのままだとHEXエディタでキーを見つけられてしまうので、C++上での難読化を試してみました。

## 難読化ツール

今回は[C/C++ Obfuscator - Obfuscate your C/C++ source code for free and online](https://picheta.me/obfuscator)を利用しています。オンラインでC/C++のコードを難読化してくれるサービスです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/ddf4edd8-88db-291b-5151-8f5f9ea154ac.png)


これを使ってキーを難読化し、C++のコード中に埋め込んでみます。

Android SDKの導入周りは[前回と変わりません](https://qiita.com/goofmint/items/09c78dcf1fd664b85a37)が、念のため書いておきます。

## Androidプロジェクトを作る

C++に対応するため、Androidプロジェクトを作成する際にNative C++を選択します。

![ScreenShot_ 2021-11-04 18.17.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/db7ec83c-8b27-a87c-e1aa-f5296136c359.png)


言語はKotlinで問題ありません。

![ScreenShot_ 2021-11-04 18.18.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/782fbe04-e27a-8042-85af-be26ef822519.png)


## native-lib.cppの編集

`app/cpp` ディレクトリにnative-lib.cppというファイルがありますので、これを編集します。

![ScreenShot_ 2021-11-05 11.53.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/2ae8d788-62b8-9ed9-230c-585e4742e6d6.png)


今回はアプリケーションキーとクライアントキーを返却する関数を追加します。それぞれ[C/C++ Obfuscator - Obfuscate your C/C++ source code for free and online](https://picheta.me/obfuscator)で生成した難読化文字列に変換します。

```cpp
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_kotlinhiddenkey_MainActivity_applicationKey(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "\x48""e\154l\x6F"" \127o\x72""l\144!";
    return env->NewStringUTF(hello.c_str());
}

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_kotlinhiddenkey_MainActivity_clientKey(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "\x48""e\154l\x6F"" \127o\x72""l\144!";
    return env->NewStringUTF(hello.c_str());
}
```

これで関数の追加が完了です。

## Android SDKを導入する

次にNCMBのAndroid SDKを導入します。これは[イントロダクション (Android) : クイックスタート | ニフクラ mobile backend](https://mbaas.nifcloud.com/doc/current/introduction/quickstart_android.html)に沿って進めてもらえば問題ありません。具体的には次の手順を行います。

1. GitHubのリリースページからSDKをダウンロードして展開
2. NCMB.jarをapp/libsディレクトリに入れる
3. app/build.gradle の編集
4. app/src/main/AndroidManifest.xml の編集

![ScreenShot_ 2021-11-05 11.55.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/b38117d4-afb7-402b-7c72-b02c9ea583fa.png)


## コードを修正する

MainActivity.kt を修正します。まずSDKを読み込みます。

```kotlin
import com.nifcloud.mbaas.core.NCMB;
```

次にC++ファイルで設定した関数を定義します。

```kotlin
class MainActivity : AppCompatActivity() {
  // 省略
  external fun applicationKey(): String
  external fun clientKey(): String
}
```

そしてNCMBの初期化時に呼び出します。

```kotlin
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    NCMB.initialize(this.getApplicationContext(),applicationKey(),clientKey());
    // 省略
}
```

これで完了です。

## 実行する

初期化が終われば、後は普段のNCMB利用と同じく使えます。

```kotlin
// クラスのNCMBObjectを作成
val obj = NCMBObject("TestClass")
// オブジェクトの値を設定
try {
    obj.put("message", "Hello, NCMB!")
} catch (e: NCMBException) {
    e.printStackTrace()
}
// データストアへの登録
obj.saveInBackground { e ->
    if (e != null) {
        //保存に失敗した場合の処理
        Log.d("Main", "Data creates failed.");
    } else {
        //保存に成功した場合の処理
        Log.d("Main", "Data creates successful.");
    }
}
```

これで完了です。

## ビルドする

後は普段通りAndroidアプリをビルドします。

## HEXエディタで見てみる

C++のコード上では難読化されているのですが、コンパイルされた結果を見てみると、元の文字列が復元されてしまっているのが分かります。単純に検索しても引っかかるので、難読化ツールの導入は意味がなさそうです。

![ScreenShot__2021-11-11_10_36_14.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/2531d7e6-7193-b119-43f7-8d728eea8716.jpeg)


## 関数を分割する

苦肉の策ですが、例えば関数を2つに分割してみます。

```cpp
#include <jni.h>
#include <string>

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_kotlinhiddenkey_MainActivity_applicationKey1(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "アプリケーションキーの前半";
    return env->NewStringUTF(hello.c_str());
}

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_kotlinhiddenkey_MainActivity_clientKey1(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "クライアントキーの前半";
    return env->NewStringUTF(hello.c_str());
}

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_kotlinhiddenkey_MainActivity_applicationKey2(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "アプリケーションキーの後半";
    return env->NewStringUTF(hello.c_str());
}

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_kotlinhiddenkey_MainActivity_clientKey2(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "クライアントキーの後半";
    return env->NewStringUTF(hello.c_str());
}
```

Kotlin側でも関数を2つ分定義します。

```kotlin
external fun applicationKey1(): String
external fun applicationKey2(): String
external fun clientKey1(): String
external fun clientKey2(): String
```

そして呼び出し側で文字列を連結します。

```kotlin
var applicationKey = applicationKey1() + applicationKey2();
var clientKey = clientKey1() + clientKey2();
NCMB.initialize(this.getApplicationContext(),applicationKey,clientKey);
```

このように分けた状態でビルドすると、HEXエディタ上で見ても分割されています。こうなっていれば、見つけづらくなるかも知れません。

![ScreenShot__2021-11-11_10_43_10.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/436d91a4-8df4-bc10-68d6-ee097c2dfc71.jpeg)


## まとめ

Androidアプリでのキー隠蔽化については、C++を利用するのが一番だと思いますが、キーを関数に直書きしているとHEXエディタでそれっぽい文字列を探される可能性があります。文字列を分割して呼び出すなど、多少の工夫がいりそうです。

[イントロダクション (Android) : クイックスタート | ニフクラ mobile backend](https://mbaas.nifcloud.com/doc/current/introduction/quickstart_android.html)