Kotlinを使ってAndroidアプリを作った際に、NCMBのアプリケーションキーとクライアントキーをどこまで隠蔽化できるのか試しています。前回はC++を使ってコンパイル、隠蔽化する方法を試しました。そのままだとHEXエディタでキーを見つけられてしまうので、C++上での難読化を試してみました。
難読化ツール
今回はC/C++ Obfuscator - Obfuscate your C/C++ source code for free and onlineを利用しています。オンラインでC/C++のコードを難読化してくれるサービスです。
これを使ってキーを難読化し、C++のコード中に埋め込んでみます。
Android SDKの導入周りは前回と変わりませんが、念のため書いておきます。
Androidプロジェクトを作る
C++に対応するため、Androidプロジェクトを作成する際にNative C++を選択します。
言語はKotlinで問題ありません。
native-lib.cppの編集
app/cpp
ディレクトリにnative-lib.cppというファイルがありますので、これを編集します。
今回はアプリケーションキーとクライアントキーを返却する関数を追加します。それぞれC/C++ Obfuscator - Obfuscate your C/C++ source code for free and onlineで生成した難読化文字列に変換します。
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に沿って進めてもらえば問題ありません。具体的には次の手順を行います。
- GitHubのリリースページからSDKをダウンロードして展開
- NCMB.jarをapp/libsディレクトリに入れる
- app/build.gradle の編集
- app/src/main/AndroidManifest.xml の編集
コードを修正する
MainActivity.kt を修正します。まずSDKを読み込みます。
import com.nifcloud.mbaas.core.NCMB;
次にC++ファイルで設定した関数を定義します。
class MainActivity : AppCompatActivity() {
// 省略
external fun applicationKey(): String
external fun clientKey(): String
}
そしてNCMBの初期化時に呼び出します。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
NCMB.initialize(this.getApplicationContext(),applicationKey(),clientKey());
// 省略
}
これで完了です。
実行する
初期化が終われば、後は普段のNCMB利用と同じく使えます。
// クラスの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++のコード上では難読化されているのですが、コンパイルされた結果を見てみると、元の文字列が復元されてしまっているのが分かります。単純に検索しても引っかかるので、難読化ツールの導入は意味がなさそうです。
関数を分割する
苦肉の策ですが、例えば関数を2つに分割してみます。
#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つ分定義します。
external fun applicationKey1(): String
external fun applicationKey2(): String
external fun clientKey1(): String
external fun clientKey2(): String
そして呼び出し側で文字列を連結します。
var applicationKey = applicationKey1() + applicationKey2();
var clientKey = clientKey1() + clientKey2();
NCMB.initialize(this.getApplicationContext(),applicationKey,clientKey);
このように分けた状態でビルドすると、HEXエディタ上で見ても分割されています。こうなっていれば、見つけづらくなるかも知れません。
まとめ
Androidアプリでのキー隠蔽化については、C++を利用するのが一番だと思いますが、キーを関数に直書きしているとHEXエディタでそれっぽい文字列を探される可能性があります。文字列を分割して呼び出すなど、多少の工夫がいりそうです。