由于使用NDK开发,可以中间的数据放入到so中,这样是的关键数据更安全。因为破解原生代码相对来说太容易,而so文件相对来说门槛较高。
我们本篇就是从安全角度来使用NDK开发,将重要的数据放入NDK中,同时将重要的加密也放入到NDK开发,这样在一定程度上可以保证APP应用的安全。
在 上篇文章 中,我们可以通过NDK的方法来获取APK的包名。
extern "C" JNIEXPORT jstring JNICALL Java_me_cyning_helloworld_NativeUtils_getPackageName( JNIEnv *env, jclass clazz, jobject instance) { jclass nativeClass = env->GetObjectClass(instance); jmethodID jmethodID1 = env->GetMethodID(nativeClass, "getPackageName", "()Ljava/lang/String;"); jstring packageName = static_cast<jstring>(env->CallObjectMethod(instance, jmethodID1)); return packageName; }
Java层调用:
public static native String getPackageName(Context context);
但是在实际的应用中,能不能不传Context来获取到全文的Context呢?答案是可以的!
Class<?> activityThreadClzz = Class.forName("android.app.ActivityThread"); Method currentApplication = activityThreadClzz.getMethod("currentApplication"); Application application = (Application) currentApplication.invoke(null);
我们可以参考: ActivityThread 。由于 currentApplication
是个静态方法,可以通过反射获取到一个Application对象。
结合之前的代码我们可以合并如下代码:
static jobject getApplication(JNIEnv *env){ jobject application = NULL; jclass activity_thread_clz = env->FindClass("android/app/ActivityThread"); if (activity_thread_clz != NULL) { // 得到ActivityThread的currentApplication的静态方法id jmethodID currentApplication = env->GetStaticMethodID( activity_thread_clz, "currentApplication", "()Landroid/app/Application;"); if (currentApplication != NULL) { // 调用ActivityThread的静态方法,返回结果application application = env->CallStaticObjectMethod(activity_thread_clz, currentApplication); } else { LOGE("Cannot find method: currentApplication() in ActivityThread."); } // 释放内存 env->DeleteLocalRef(activity_thread_clz); } else { LOGE("Cannot find class: android.app.ActivityThread"); } return application; } static jstring getPackageName( JNIEnv *env,jobject instance ){ jclass nativeClass = env->GetObjectClass(instance); jmethodID jmethodID1 = env->GetMethodID(nativeClass, "getPackageName", "()Ljava/lang/String;"); jstring packageName = static_cast<jstring>(env->CallObjectMethod(instance, jmethodID1)); return packageName; } //根据context来获取包名 extern "C" JNIEXPORT jstring JNICALL Java_me_cyning_helloworld_NativeUtils_getPackageName( JNIEnv *env, jclass clazz) { return getPackageName(env, getApplication(env)); }
但是得到包名就安全了么,NO!有些”居心不良”的人可能会改我的包签名,好了那怎么就校验下包签名,不合法的APP让它直接GG。
怎么获取包签名信息呢?
怎么获取一个APP的签名信息呢?还是和获取包名一样,我们需要拿到在java上的实现。
PackageManager pm = context.getPackageManager(); PackageInfo pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES); Signature[] signatures = pi.signatures; Signature signature0 = signatures[0]; String string = signature0.toCharsString();
用NDK怎么实现呢?
若是你不知道C怎么调用java,这是个很好的学习范例:
extern "C" JNIEXPORT jint JNICALL Java_me_cyning_helloworld_NativeUtils_getSignature(JNIEnv *env, jclass clazz) { // 获取context jobject context_obj = getApplication(env); // 获取到context的类 jclass context_clazz = env->GetObjectClass(context_obj); jstring packageName = getPackageName(env, context_obj); //获取到context中的getPackageManager的方法id jmethodID getPackageManagerMethod = env->GetMethodID(context_clazz, "getPackageManager", "()Landroid/content/pm/PackageManager;"); // 调用getPackageManager方法,获取到PackageManager对象 jobject pm_obj = env->CallObjectMethod(context_obj, getPackageManagerMethod); jclass pm_clazz = env->GetObjectClass(pm_obj); jmethodID getPackageInfoMethod = env->GetMethodID(pm_clazz, "getPackageInfo", "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;"); jobject packageInfo_obj = env->CallObjectMethod(pm_obj, getPackageInfoMethod, packageName, 64); jclass pi_clazz = env->GetObjectClass(packageInfo_obj); // Signature[] signatures = pi.signatures; 返回值是数组[] jfieldID signatures_field = env->GetFieldID(pi_clazz, "signatures", "[Landroid/content/pm/Signature;"); jobject signatures = env->GetObjectField(packageInfo_obj, signatures_field); jobjectArray signatures_array = (jobjectArray) signatures; jobject signature0 = env->GetObjectArrayElement(signatures_array, 0); jclass signature_clazz = env->GetObjectClass(signature0); jmethodID toCharsStringMethod = env->GetMethodID(signature_clazz, "toCharsString", "()Ljava/lang/String;"); jstring signature_string = (jstring)env->CallObjectMethod(signature0, toCharsStringMethod); // 释放内存 env->DeleteLocalRef(context_obj); env->DeleteLocalRef(context_clazz); env->DeleteLocalRef(packageName); env->DeleteLocalRef(pm_obj); env->DeleteLocalRef(pm_clazz); env->DeleteLocalRef(packageInfo_obj); env->DeleteLocalRef(pi_clazz); env->DeleteLocalRef(signatures); env->DeleteLocalRef(signature0); env->DeleteLocalRef(signatures_array); env->DeleteLocalRef(signature_clazz); return signature_string;
实现的对应代码:
当然了,我们也可以在NDK层实现加密,如网络请求时,将参数加密等,这样相对来说:
高效 c实现的加密相对来说速度快,效率高
具体的代码可以参考这篇 文章 ,不过可以根据需求来自己定义加密算法。