发布于2021-05-29 19:37 阅读(961) 评论(0) 点赞(24) 收藏(5)
一. 准备工作
ndk下载:https://developer.android.google.cn/ndk/downloads/
首先配置终端的ndk命令:
启动终端Terminal
输入cd~,进入当前用户的home目录
如果没有.bash_profile文件,先输入touch .bash_profile进行创建
输入open -e .bash_profile 编辑.bash_profile文件,加入下面语句
export PATH=${PATH}:“这里填你的ndk安装路径”
保存并关闭.bash_profile文件
输入source .bash_profile,更新刚刚配置的环境变量
命令行输入ndk-build,验证是否配置成功,出现下图表示配置成功
另外,Android Studio中也不要忘记设置(一般会默认设置成Android Sdk中的ndk路径,但是自己如果下载了最新的ndk,可以替换),打开File --> Project Structure:
二. JNI文件编写
创建一个含有native方法的java文件:
- package com.hyf.kaviewer.jni;
-
- /**
- * Created by heyf on 2019/2/28
- */
- public class JniTest {
-
- public static native void getSecret();
- }
通过javah命令生成.h头文件
首先需要进入到当前项目当前module的java路径下:
cd /Users/admin/Demo-Project/KAViewer/app/src/main/java
然后输入:
javah -d ../jni com.hyf.kaviewer.jni.JniTest
生成的.h文件如下所示(jni路径也是自动生成)
.h文件的内容为:
红框中的方法与java文件中所写的方法相对应,我们马上也会在.cpp文件中实现它。
创建.cpp文件
复制一份.h文件并将后缀改为.cpp
接下来编辑.cpp文件的内容:
具体代码如下:
- #include <android/log.h>
- #include <string.h>
- #include "com_hyf_kaviewer_jni_JniTest.h"
-
- #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "security", __VA_ARGS__))
- #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "security", __VA_ARGS__))
-
- static const char *SIGN = "ABCDEFGHIJKLMN";
- static const char *SECRET = "1111111111111111111";
- static int verifySign(JNIEnv *env);
-
- jstring Java_com_hyf_kaviewer_jni_JniTest_getSecret(JNIEnv *env, jclass type){
- return env->NewStringUTF(SECRET);
- }
-
- 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;
- }
- LOGE("签名不一致!");
- 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 {
- LOGE("Cannot find method: currentApplication() in ActivityThread.");
- }
- env->DeleteLocalRef(activity_thread_clz);
- } else {
- LOGE("Cannot find class: android.app.ActivityThread");
- }
-
- return application;
- }
-
- 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) {
- LOGE("分配内存失败");
- return JNI_ERR;
- }
-
- LOGI("应用中读取到的签名为:%s", sign);
- LOGI("native中预置的签名为:%s", SIGN);
- int result = strcmp(sign, SIGN);
- // 使用之后要释放这段内存
- env->ReleaseStringUTFChars(signature_str, sign);
- env->DeleteLocalRef(signature_str);
- if (result == 0) { // 签名一致
- return JNI_OK;
- }
-
- return JNI_ERR;
- }
为什么要在so文件中验证签名?是为了让我们的so库只能被我们想要的应用去调用,因此在so库中会验证预置的签名与当前调用so库的应用的签名是否一致,防止特定so库的滥用,进一步保证安全。
获取应用签名
- /**
- * 展示了如何用Java代码获取签名
- */
- private String getSign() {
- try {
- // 下面几行代码展示如何任意获取Context对象,在jni中也可以使用这种方式
- // Class<?> activityThreadClz = Class.forName("android.app.ActivityThread");
- // Method currentApplication = activityThreadClz.getMethod("currentApplication");
- // Application application = (Application) currentApplication.invoke(null);
- // PackageManager pm = application.getPackageManager();
- // PackageInfo pi = pm.getPackageInfo(application.getPackageName(), PackageManager.GET_SIGNATURES);
-
- PackageManager pm = getPackageManager();
- PackageInfo pi = pm.getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES);
- Signature[] signatures = pi.signatures;
- Signature signature0 = signatures[0];
- String str = signature0.toCharsString();
- Log.i(TAG, "getSign: " + signature0.toCharsString());
- return signature0.toCharsString();
- } catch (Exception e) {
- e.printStackTrace();
- return "";
- }
- }
并在build.gradle中加入:
- signingConfigs {
- debug {
- storeFile file("../xx.keystore")
- storePassword "xxxxxx"
- keyAlias "xx"
- keyPassword "xxxxxx"
- }
- }
可以用上述代码获取签名(可以将方法放入启动Activity中,运行后在log中获取签名)
在jni目录下创建Android.mk文件
写入以下配置:
- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_LDLIBS := -lm -llog //这一句是为了支持.cpp文件中的Log
-
- LOCAL_MODULE := security
- LOCAL_SRC_FILES := com_hyf_kaviewer_jni_JniTest.cpp
-
- include $(BUILD_SHARED_LIBRARY)
需要注意的是:
LOCAL_MODULE := security ,security是将要生成的so库的名字,可以按需要改成自己想要的
LOCAL_SRC_FILES := com_hyf_kaviewer_jni_JniTest.c ,此处需要替换成自己的c文件
在jni目录下创建Application.mk文件
写入以下配置:
APP_ABI := all
该配置表示会生成所有主流ABI类型的so库
至此,所需的文件已创建完毕:
在当前Module下的build.gradle下增加配置
在 defaultConfig{} 下增加以下配置:
- ndk {
- moduleName "security"
- }
最后生成so库
在终端中,cd到jni目录下
输入ndk-build
成功后打印如下所示
生成so库的路径如下图
PS:在使用so库的时候,方法调用处的路径和.h中的路径名一定要一致(即包名和文件名),否则会报错,因为编译器是根据这个路径去调用so库的代码
原文链接:https://blog.csdn.net/VanEasley/article/details/117338962
作者:小泽圈儿郎
链接:http://www.javaheidong.com/blog/article/207102/eca31e23991e84a70675/
来源:java黑洞网
任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任
昵称:
评论内容:(最多支持255个字符)
---无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁、焦虑,坏掉你本来就不多的热情和定力
Copyright © 2018-2021 java黑洞网 All Rights Reserved 版权所有,并保留所有权利。京ICP备18063182号-2
投诉与举报,广告合作请联系vgs_info@163.com或QQ3083709327
免责声明:网站文章均由用户上传,仅供读者学习交流使用,禁止用做商业用途。若文章涉及色情,反动,侵权等违法信息,请向我们举报,一经核实我们会立即删除!