All Projects → chenzhihui28 → SecurityDemo

chenzhihui28 / SecurityDemo

Licence: other
ndk进行简单的签名校验,密钥保护demo,android应用签名校验

Programming Languages

C++
36643 projects - #6 most used programming language
java
68154 projects - #9 most used programming language
CMake
9771 projects

Projects that are alternatives of or similar to SecurityDemo

Android Luajit Launcher
Android NativeActivity based launcher for LuaJIT, implementing the main loop within Lua land via FFI
Stars: ✭ 87 (+295.45%)
Mutual labels:  ndk, jni
Androidsecurity
Android安全实践
Stars: ✭ 150 (+581.82%)
Mutual labels:  ndk, jni
Androiddevwithcpp
Android Develop With C++
Stars: ✭ 106 (+381.82%)
Mutual labels:  ndk, jni
Jni.hpp
A modern, type-safe, header-only, C++14 wrapper for JNI
Stars: ✭ 313 (+1322.73%)
Mutual labels:  ndk, jni
ChangeVoice
NDK语音消息的变声处理
Stars: ✭ 33 (+50%)
Mutual labels:  ndk, jni
Keepalive
Fighting against force-stop kill process on Android with binder ioctl / Android高级保活
Stars: ✭ 376 (+1609.09%)
Mutual labels:  ndk, jni
Native Opencv Android Template
A tutorial for setting up OpenCV 4.5.0 (and other 4.x.y version) for Android in Android Studio with Native Development Kit (NDK) support.
Stars: ✭ 131 (+495.45%)
Mutual labels:  ndk, jni
Googleserialport
Android串口通信:抱歉,学会它真的可以为所欲为 ! ! !
Stars: ✭ 130 (+490.91%)
Mutual labels:  ndk, jni
Android Disassembler
Disassemble ANY files including .so (NDK, JNI), Windows PE(EXE, DLL, SYS, etc), linux binaries, libraries, and any other files such as pictures, audios, etc(for fun)files on Android. Capstone-based disassembler application on android. 안드로이드 NDK 공유 라이브러리, Windows 바이너리, etc,... 리버싱 앱
Stars: ✭ 250 (+1036.36%)
Mutual labels:  ndk, jni
Relinker
A robust native library loader for Android.
Stars: ✭ 2,612 (+11772.73%)
Mutual labels:  ndk, jni
Jnioor
基于C++模板函数与Fluent API设计的JNI反射库,极大的简化JNI反射调用,提高JNI开发效率与稳定性
Stars: ✭ 278 (+1163.64%)
Mutual labels:  ndk, jni
Camera2GLPreview
Android camera preview application using Camera2 API and OpenGL ES/Vulkan
Stars: ✭ 140 (+536.36%)
Mutual labels:  ndk, jni
Jni4android
JNI Generater for Android
Stars: ✭ 261 (+1086.36%)
Mutual labels:  ndk, jni
Anyndk
🔥 Android native library, make your development faster and easier. Android各种native库,让你的开发更快更简单
Stars: ✭ 35 (+59.09%)
Mutual labels:  ndk, jni
Jpegkit Android
Efficient JPEG operations for Android without the risk of an OutOfMemoryException.
Stars: ✭ 154 (+600%)
Mutual labels:  ndk, jni
NativeGL demo
此程序使用android native代码实现EGL,并使用SurfaceView作为OpenGL绘图窗口。即可提高图形渲染性能,又可使用java层定义的图形界面
Stars: ✭ 43 (+95.45%)
Mutual labels:  ndk, jni
premake-android-studio
premake5 module for android-studio and gradle build.
Stars: ✭ 24 (+9.09%)
Mutual labels:  ndk, jni
AverageFaceDemo
Average face on android
Stars: ✭ 31 (+40.91%)
Mutual labels:  ndk
android-PageFlip-JNI
JNI library of Page Flip
Stars: ✭ 14 (-36.36%)
Mutual labels:  jni
android-webrtc-aecm
An acoustic echo cancellation for android, based on webrtc aecm algorithms
Stars: ✭ 24 (+9.09%)
Mutual labels:  jni

SecurityDemo

ndk进行简单的签名校验,密钥保护

使用Cmake进行NDK开发(应用签名校验、密钥保护、so文件适配相关)

NDK官方指南

CMake官方文档

CMake译文

Google官方samples

从HelloWorld开始咯

新建项目,勾选include c++ support


建立完成之后,看看现在的项目文件结构


build.gradle


多了一个.externalNativeBuild文件夹 main下多了个cpp文件夹 以及app里面多了CMakeLists.txt build.gradle文件多了两处externalNativeBuild块 defaultConfig外面的那个externalNativeBuild块主要是指定CMakeLists.txt的文件路径 defaultConfig里面的那个externalNativeBuild块主要填写CMake的命令参数。即由 arguments 中的参数最后转化成一个可执行的 CMake 的命令,可以在.externalNativeBuild/cmake/debug/{abi}/cmake_build_command.txt 中查到

来看看CMakeLists.txt


CMakeLists文件里面指出了需要编译的文件

默认已经帮我们生成了一个jni方法,返回一个helloworld字符串


运行结果:

做一下简单的密钥保存

先专门弄一个类来生命native方法和加载so库,就叫SecurityUtil类吧 放在包名的目录下(注意,定义native方法的时候,跟加载so库所在的类有关系,比如这里的SecurityUtil类,我放在com.a.b目录下,在这个类的静态代码块加载库,System.loadLibrary("native-lib");那么,定义native方法的格式就应该是Java_com_a_b_方法名,而当我放在com.a.b.c下,那native方法的格式就是Java_com_a_b_c_方法名,具体可以查一下jni方法的命名) 在cpp文件夹新建一个security.cpp文件,改一下CMakeLists文件让他编译我们的cpp文件,新增一个getSecret方法,运行

运行效果:


然后可以看到包名/build/intermediate/cmake目录下已经生成了so文件

签名校验

上一步做的密钥保存生成的so库文件其实也不安全,由于jni的机制是反射实现的java端和native端的互相调用,所以在打包时候不能混淆native方法,所以只需要反编译提取出so文件和知道相应的native方法名,自己新建一个应用load一下那个库调用一下方法,那个密钥串就能简单被debug出来,所以我们一般会做应用的签名校验,当签名不一致时直接崩溃就好了。

java端获取应用签名的方法

PackageManager pm = context.getPackageManager();
PackageInfo pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
Signature[] signatures = pi.signatures;
Signature signature0 = signatures[0];
signature0.toCharsString();

获取签名跟context对象有关,所以假如重写一下context和packagemanager,还是有可能仿冒正确的context对象,这里使用反射ActivityThread类,然后通过invoke反射调用currentApplication方法获取当前的context对象,避免由参数传入context对象的方式增大破解的难度 对应到native端,获取context的代码就变成了

static jobject getApplication(JNIEnv *env) {
    jobject application = NULL;
    jclass activity_thread_clz = env->FindClass("android/app/ActivityThread");
    if (activity_thread_clz != NULL) {
        jmethodID currentApplication = env->GetStaticMethodID(
                activity_thread_clz, "currentApplication", "()Landroid/app/Application;");
        if (currentApplication != NULL) {
            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;
}

验证签名的时机,放在jni的onload方法,也就是当加载动态库的时候就进行签名校验,跟内置的串进行对比,校验不通过直接崩溃。

所以最后的cpp文件如下

#include <android/log.h>
#include <string>
#include <string.h>
#include <jni.h>

#define LOGINFO(...) ((void)__android_log_print(ANDROID_LOG_INFO, "security", __VA_ARGS__))
#define LOGERROR(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "security", __VA_ARGS__))

static int verifySign(JNIEnv *env);

jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env = NULL;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) {
        return JNI_ERR;
    }
    if (verifySign(env) == JNI_OK) {
        return JNI_VERSION_1_4;
    }
    LOGERROR("签名不一致!");
    return JNI_ERR;
}

static jobject getApplication(JNIEnv *env) {
    jobject application = NULL;
    jclass activity_thread_clz = env->FindClass("android/app/ActivityThread");
    if (activity_thread_clz != NULL) {
        jmethodID currentApplication = env->GetStaticMethodID(
                activity_thread_clz, "currentApplication", "()Landroid/app/Application;");
        if (currentApplication != NULL) {
            application = env->CallStaticObjectMethod(activity_thread_clz, currentApplication);
        } else {
            LOGERROR("Cannot find method: currentApplication() in ActivityThread.");
        }
        env->DeleteLocalRef(activity_thread_clz);
    } else {
        LOGERROR("Cannot find class: android.app.ActivityThread");
    }

    return application;
}


static const char *SIGN = "308203533082023ba0030201020204691f1618300d06092a864886f70d01010b0500305a310d300b060355040613046368656e310d300b060355040813046368656e310d300b060355040713046368656e310d300b060355040a13046368656e310d300b060355040b13046368656e310d300b060355040313046368656e301e170d3138303432353038323530355a170d3433303431393038323530355a305a310d300b060355040613046368656e310d300b060355040813046368656e310d300b060355040713046368656e310d300b060355040a13046368656e310d300b060355040b13046368656e310d300b060355040313046368656e30820122300d06092a864886f70d01010105000382010f003082010a02820101008001ead218d523543023e195c065fb807f06313a915f510e59d556c55f192163a5acd4300851ae738a601c2b4a6284beb900c31338d4ce36dd3fe490c16560dc4dfca13685a1993c6705976797a5a23841edd261c95a419da6e62ebe17a25b787e1ff2097a9b112f48a3fe03b7777b9833799680bae0a9a1d423296b31a87e65758b6fb56f9ea78c4d0537924663563c8cc59c2877c4ee58704b03f3cd5bdb1383084064be54b0a8de408ca51ecb0c68194fc6adb643d5827053371a34d75d8c98ff7edc4d929747afe48a7e6701d9dfd7897a221ba82b63c27bf2ce8c0784c5810f425526e28b840c429d9f0984b44671d3d5a1b77827f529c7aad6877f3c450203010001a321301f301d0603551d0e0416041403a7a9f045b7ab586c6102c49ced45e8879d350a300d06092a864886f70d01010b050003820101007dc1ae5fabc39101d74987f535be17e9c80b71daf55193b4c6abd381adf59042dabeeebd59d8f2ad1cbe0dd4feaee803e62b9b4b425a0936a4041245b7893a51c40fe0f10ea67a0a00e6360c25c703c5801f8e8740c544ce3d3874620dc26efbf5331020d30ac9e0ae1b217ab841959386a56aab61981ba01a1d62ede59866d936d1ade91d6723ea5569128a6bd5a8158733bb18499f8100a29beee19713be64b82ba348bb4d8eb5591cd5b09323746ccd5b9b115c6c1462f07165070eab6cfd9f18af717e94f384059b72ee3866553adc6d248b7d4a7f945666edb822eb20d2229d3b8c8b1038b1f11640b7a00e8f79623ffedf697da528af1ac828b009af53";

static int verifySign(JNIEnv *env) {
    // Application object
    jobject application = getApplication(env);
    if (application == NULL) {
        return JNI_ERR;
    }
    // Context(ContextWrapper) class
    jclass context_clz = env->GetObjectClass(application);
    // getPackageManager()
    jmethodID getPackageManager = env->GetMethodID(context_clz, "getPackageManager",
                                                   "()Landroid/content/pm/PackageManager;");
    // android.content.pm.PackageManager object
    jobject package_manager = env->CallObjectMethod(application, getPackageManager);
    // PackageManager class
    jclass package_manager_clz = env->GetObjectClass(package_manager);
    // getPackageInfo()
    jmethodID getPackageInfo = env->GetMethodID(package_manager_clz, "getPackageInfo",
                                                "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
    // context.getPackageName()
    jmethodID getPackageName = env->GetMethodID(context_clz, "getPackageName",
                                                "()Ljava/lang/String;");
    // call getPackageName() and cast from jobject to jstring
    jstring package_name = (jstring) (env->CallObjectMethod(application, getPackageName));
    // PackageInfo object
    jobject package_info = env->CallObjectMethod(package_manager, getPackageInfo, package_name, 64);
    // class PackageInfo
    jclass package_info_clz = env->GetObjectClass(package_info);
    // field signatures
    jfieldID signatures_field = env->GetFieldID(package_info_clz, "signatures",
                                                "[Landroid/content/pm/Signature;");
    jobject signatures = env->GetObjectField(package_info, signatures_field);
    jobjectArray signatures_array = (jobjectArray) signatures;
    jobject signature0 = env->GetObjectArrayElement(signatures_array, 0);
    jclass signature_clz = env->GetObjectClass(signature0);

    jmethodID toCharsString = env->GetMethodID(signature_clz, "toCharsString",
                                               "()Ljava/lang/String;");
    // call toCharsString()
    jstring signature_str = (jstring) (env->CallObjectMethod(signature0, toCharsString));

    // release
    env->DeleteLocalRef(application);
    env->DeleteLocalRef(context_clz);
    env->DeleteLocalRef(package_manager);
    env->DeleteLocalRef(package_manager_clz);
    env->DeleteLocalRef(package_name);
    env->DeleteLocalRef(package_info);
    env->DeleteLocalRef(package_info_clz);
    env->DeleteLocalRef(signatures);
    env->DeleteLocalRef(signature0);
    env->DeleteLocalRef(signature_clz);

    const char *sign = env->GetStringUTFChars(signature_str, NULL);
    if (sign == NULL) {
        LOGERROR("分配内存失败");
        return JNI_ERR;
    }
//发布记得去掉log!!!!!!!
//    LOGINFO("应用中读取到的签名为:%s", sign);
//    LOGINFO("native中预置的签名为:%s", SIGN);
    int result = strcmp(sign, SIGN);
//    LOGINFO("strcmp:%d", result);
    // 使用之后要释放这段内存
    env->ReleaseStringUTFChars(signature_str, sign);
    env->DeleteLocalRef(signature_str);
    if (result == 0) { // 签名一致
        return JNI_OK;
    }

    return JNI_ERR;
}


extern "C" JNIEXPORT jstring

JNICALL
Java_com_chenzhihui_demo_SecurityUtil_getSecret(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "hahahahasecrethahahahaha";
    return env->NewStringUTF(hello.c_str());
}

换一个签名再运行或者改一下内置的签名串让它不相等:



这样就完成了签名校验过程 打个包会发现 包名/build/intermediate/cmake目录下已经生成了各个abi对应的so文件,当然也可以在gradle文件指定需要的abi

so文件相关

不同 Android 手机使用不同的 CPU,因此支持不同的指令集。CPU 与指令集的每种组合都有其自己的应用二进制界面(或 ABI)。 ABI 可以非常精确地定义应用的机器代码在运行时如何与系统交互。 您必须为应用要使用的每个 CPU 架构指定 ABI。
官网对abi的介绍

兼容方面

对于CPU来说,不同的架构并不意味着一定互不兼容,根据目前Android共支持七种不同类型的CPU架构,其兼容特点可总结如下:

  • armeabi设备只兼容armeabi;
  • armeabi-v7a设备兼容armeabi-v7a、armeabi;
  • arm64-v8a设备兼容arm64-v8a、armeabi-v7a、armeabi;
  • X86设备兼容X86、armeabi;
  • X86_64设备兼容X86_64、X86、armeabi;
  • mips64设备兼容mips64、mips;
  • mips只兼容mips; armeabi的so能运行在除了mips和mips64的设备上(在非armeabi上性能有所损耗) 64位的CPU架构总能向下兼容其对应的32位指令集

适配方面

  • ARM架构几乎垄断,而且绝大部分都是armeabi-v7a、arm64-v8a,几乎可以不考虑X86、X86_64、mips、mips64架构
  • 假如你只提供了armeabi,而没有提供v7a,v8a,且某些so文件在v7a或v8a架构中使用armeabi的so时性能差距明显,则可以考虑单独把那份so的v7a,v8a的so文件也放进armeabi的文件夹,通过代码判断当前cpu架构然后再来加载对应的so文件
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].