美文网首页Android-NDK/JNI
Android JNI NDK C++ so敏感信息保护

Android JNI NDK C++ so敏感信息保护

作者: _九卿_ | 来源:发表于2020-04-03 17:30 被阅读0次

    一. 前言

    这几天在做项目的时候,需要对敏感信息进行保护,防止反编译泄露,做个笔记吧。

    二. 集成JNI

    JNI的全称是Java Native Interface,它允许Java语言可以按照一定的规则去调用其他语言,与其进行交互。

    1. 配置NDK环境

    没有使用CMake,使用了NDK方式

    NDK配置
    为何要用到NDK?

    概括来说主要分为以下几种情况:

    1. 代码的保护。由于 apk 的 java 层代码很容易被反编译,而 C/C++ 库反汇难度较大。
    2. 提高程序的执行效率。将要求高性能的应用逻辑使用 C 开发,从而提高应用程序的执行效率。
    3. 便于移植。用 C/C++ 写得库可以方便在其他的嵌入式平台上再次使用。
    2. 配置app下的build.gradle
    3. 在项目下gradle.properties文件添加
    android.useDeprecatedNdk=true
    
    4. 调用c的java类

    这个工具类的地方必须跟你将来要调用so库文件的工具类的地方的包名必须一致

    public class TestUtils {
        static {
            //加载本地库文件 (省略前缀lib和后缀.so)
            System.loadLibrary("testUtil");//testUtil就是上一步在build.gradle中配置的moduleName so的名字
        }
        public static native String test(Object obj, boolean isRelease);
    }
    
    5. 生成JNI的头文件

    我们先了解下NDK与JNI的关系

    在JNI开发中我们的Java Native方法都会对应到c语言中的Native方法。上一步我们在Java中已声明了native方法,现在需要用javah命令来生成头文件。

    有两种方式:
    1. 用javah命令生成C语言头文件

    切换到源文件根目录下: cd app/src/main/java
    生成C语言头文件: javah -d jni -jni com.ghp.test.TestUtils

    2. 扩展工具生成C语言头文件

    AS扩展工具, 可以让我们自定义一些命令行工具.比如说: 自动生成JNI头文件, 打debug/release包等等.
    下面就来看看如何利用Android Studio External Tools机制类实现这个功能.

    a. 操作步骤:

    Perferences -> External Tools -> 点击"加号"添加一个扩展工具 -> 填写工具信息
    如下图所示:

    b. 工具使用:

    操作步骤: 在native方法所在的类上右键 -> My Tools -> 点击javah (generate c header file), 如下图:


    这时会在main目录下面生成jni目录且在此目录下生成C头文件.
    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class com_ghp_test_TestUtils */
    
    #ifndef _Included_com_ghp_test_TestUtils
    #define _Included_com_ghp_test_TestUtils
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
     * Class:     com_ghp_test_TestUtils
     * Method:    test
     * Signature: (Ljava/lang/Object;Z)Ljava/lang/String;
     */
    JNIEXPORT jstring JNICALL Java_com_ghp_test_TestUtils_test
      (JNIEnv *, jclass, jobject, jboolean);
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    
    6. 创建c/c++文件

    在jni文件夹下创建一个c/c++文件

    testUtils.c实现了com_ghp_test_TestUtils.h文件,验签ok返回ok。

    注意路径不要写错了,生成的 .h 文件里包含自动生成的一些方法,方法名称一一应对 Java native 方法。

    在这里include了sign.h,是验证签名方法的头文件,从目录看头文件的实现是sign.cpp。是c调用c++,需要在sign.h里添加extern "c",不然会报has a different language linkage。

    还有个Android.mk文件,这里配置了so文件的name(同时ndk里配置moduleName将失效),和使用的c/c++文件

    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    
    LOCAL_MODULE := testUtil //so库名
    LOCAL_SRC_FILES := testUtils.c sign.cpp //逻辑文件罗列
    
    include $(BUILD_SHARED_LIBRARY)
    

    同时需要在module的build.gradle里配置:

    // ndk-build模式
       externalNativeBuild {
            ndkBuild {
                path "src/main/jni/Android.mk"
            }
        }
    

    验签的实现部分:

    1. java先获取APP签名
    public static String getSignature(Context context)
            {
                try {
                    /** 通过包管理器获得指定包名包含签名的包信息 **/
                    PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
                    /******* 通过返回的包信息获得签名数组 *******/
                    Signature[] signatures = packageInfo.signatures;
                    /******* 循环遍历签名数组拼接应用签名 *******/
                    return signatures[0].toCharsString();
                    /************** 得到应用签名 **************/
                } catch (PackageManager.NameNotFoundException e) {
                    e.printStackTrace();
                }
                return null;
    }
    
    1. c++代码的实现
    #include <jni.h>
    #include <string.h>
    #include <stdio.h>
    #include "sign.h"
    
    /**
     * 发布的app 签名,只有和本签名一致的app 才会返回 JNI_TRUE
     * 这个RELEASE_SIGN的值是上一步用java代码获取的值
     */
    const char *RELEASE_SIGN = "3082036f3……………e6d0516a";
    
    int signResult(JNIEnv *env, jobject contextObject) {
        jclass native_class = env->GetObjectClass(contextObject);
        jmethodID pm_id = env->GetMethodID(native_class, "getPackageManager", "()Landroid/content/pm/PackageManager;");
        jobject pm_obj = env->CallObjectMethod(contextObject, pm_id);
        jclass pm_clazz = env->GetObjectClass(pm_obj);
        // 得到 getPackageInfo 方法的 ID
        jmethodID package_info_id = env->GetMethodID(pm_clazz, "getPackageInfo",
                                                     "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
        jclass native_classs = env->GetObjectClass(contextObject);
        jmethodID mId = env->GetMethodID(native_classs, "getPackageName", "()Ljava/lang/String;");
        jstring pkg_str = static_cast<jstring>(env->CallObjectMethod(contextObject, mId));
        // 获得应用包的信息
        jobject pi_obj = env->CallObjectMethod(pm_obj, package_info_id, pkg_str, 64);
        // 获得 PackageInfo 类
        jclass pi_clazz = env->GetObjectClass(pi_obj);
        // 获得签名数组属性的 ID
        jfieldID signatures_fieldId = env->GetFieldID(pi_clazz, "signatures", "[Landroid/content/pm/Signature;");
        jobject signatures_obj = env->GetObjectField(pi_obj, signatures_fieldId);
        jobjectArray signaturesArray = (jobjectArray) signatures_obj;
        jsize size = env->GetArrayLength(signaturesArray);
        jobject signature_obj = env->GetObjectArrayElement(signaturesArray, 0);
        jclass signature_clazz = env->GetObjectClass(signature_obj);
    
        //第一种方式--检查签名字符串的方式
        jmethodID string_id = env->GetMethodID(signature_clazz, "toCharsString", "()Ljava/lang/String;");
        jstring str = static_cast<jstring>(env->CallObjectMethod(signature_obj, string_id));
        char *c_msg = (char *) env->GetStringUTFChars(str, 0);
    
        return strcmp(c_msg, RELEASE_SIGN) == 0 ? JNI_TRUE : JNI_FALSE;//校验签名一致
    }
    
    7. 使用so文件

    build项目在目录下找到so文件

    将他们拷贝到APP的libs对应文件夹,去除C文件的使用,同时使用的项目里build.gradle也需要配置abiFilters。
    在sign.cpp,可以添加判断当前 APP 是否为合法应用,对验签增加MD5。

    到这里觉得可以了呢,当看这里《逆向札记-利用IDA简单过so签名校验》,给这篇文章跪了,详细的介绍了破解安卓NDK端native方法动态JNI反射so文件签名校验的方法,敏感信息放so文件只签验看来是不行了,好吧,其他敏感信息再加一层RSA加解密处理吧……

    相关文章

      网友评论

        本文标题:Android JNI NDK C++ so敏感信息保护

        本文链接:https://www.haomeiwen.com/subject/dbshphtx.html