看到这个题目似乎有点啰嗦,分两块:
一是关于JNI开发的静动态注册,因为涉及到一点逆向上的安全问题,因此有必要进行细细的琢磨和加以区别;
二是在关于JNI的开发过程中对于用.c与.cpp不同文件时注意的一系列问题和原理。
由于也没有太多的原理可讲,我们直接拿例子说事,直接从实现篇说起比较好,这块我不会选择像网上那些简单的输出字符串的这种例子,因为起不到一定的理解作用。
由于作者编辑水平太差,如果觉得看的费劲直接点击这里下文档:点击打开链接
实现篇:
Android Studio JNI 的静态注册:
第一步:
在MainActivity.java下面新建一个类addadd.java代码如下:
package com.example.zbb.jniregister;
/**
* Created by ZBB on 2016/9/30.
*/
public class addadd {
static {
System.loadLibrary("add");
}
public static native int addint(int a,int b);
}
这里就是java层与本地层交互时的使用System.loadLibrary()来进行加载add.so这个文件;
第二步:
Java的native方法与C/C++代码函数是通过Java_<包名>_<类名>_<方法名>这种方式对应的,即它是静态注册的;因此下一步
我们要使用javah命令生成以上的头文件:
在main目录下新建jni目录把对应生成的头文件拷贝进去,然后新建与上面
System.loadLibrary("add")相同的add.c文件,在里面实现addint这个方法;
<strong>第三步</strong>:
在app目录下面实现更改build.gradle文件;在defaultConfig里面增加:
<pre name="code" class="cpp" style="font-size: 24px;">ndk{
moduleName "add"
abiFilters "armeabi", "armeabi-v7a", "x86"
}
因为Android studio中是凭借 build.gradle来自动编译这个add.c文件
当然这一点是与eclipse有点区别,但是也可以手动编译这个文件:
具体如下:
1.在jni目录下面新建Android.mk文件与eclipse下一样;
2.在build.gradle下面android{}里面加以下 :
sourceSets {
main {
jni.srcDirs = []
jniLibs.srcDirs = ['src/main/libs']
}}
3.在jni目录下进行ndk-build编译
android.useDeprecatedNdk=true;
<strong>第五步</strong>:
在MainActivity下面补充完整功能,调用本地层功能,就OK了,总之比较简单,
有问题百度就一定可以解决。
<strong>Android studio JNI的动态注册</strong>:<span style="font-family: Arial, Helvetica, sans-serif;"> </span>
静态注册不好处有二:
其一:比较麻烦,函数名长不好管理,形式固定;
其二:正如前一篇文章说到的对于逆向着来说比较容易找到你的核心函数,没有看的可以点击:http://blog.csdn.net/feibabeibei_beibei/article/details/52668534
因此我们下来讲动态注册:
直接上代码 将上面的代码改为:
#include "com_example_zbb_jniregister_addadd.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <jni.h>
#include <assert.h>
//静态注册JNI
/*JNIEXPORT jint JNICALL Java_com_example_zbb_jniregister_addadd_addint
(JNIEnv *env, jobject obj, jint a, jint b){
return a+b;
}*/
//动态注册JNI
/**
* 方法对应表
*/
jint addint1(JNIEnv *env, jobject obj, jint a, jint b){
return a+b;
}
static JNINativeMethod gMethods[] = {
{"addint", "(II)I", (void*)addint1},//绑定
};
/*
* 为某一个类注册本地方法
*/
static int registerNativeMethods(JNIEnv* env
, const char* className
, JNINativeMethod* gMethods, int numMethods) {
jclass clazz;
clazz = (*env)->FindClass(env, className);
if (clazz == NULL) {
return JNI_FALSE;
}
if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
return JNI_FALSE;
}
return JNI_TRUE;
}
/*
* 为所有类注册本地方法
*/
static int registerNatives(JNIEnv* env) {
const char* kClassName = "com/example/zbb/jniregister/addadd";//指定要注册的类
return registerNativeMethods(env, kClassName, gMethods,
sizeof(gMethods) / sizeof(gMethods[0]));
}
/*
* System.loadLibrary("lib")时调用
* 如果成功返回JNI版本, 失败返回-1
*/
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved){
JNIEnv* env = NULL;
jint result = -1;
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
assert(env != NULL);
if (!registerNatives(env)) {//注册
return -1;
}
//成功
result = JNI_VERSION_1_6;
return result;
}
很简单也很好理解,唯一注意的就是在方法注册映射表这一块,在前一篇已经说过这里就不说了。
OK,完成了上面的问题,但是这里有个问题困扰了我好长时间,在使用.c和.cpp文件时的关于JNIEnv这个问题:
在C中:
使用JNIEnv* env要这样 (*env)->方法名(env,参数列表)
使用JavaVM* vm要这样 (*vm)->方法名(vm,参数列表)
在C++中:
使用JNIEnv* env要这样 env->方法名(参数列表)
使用JavaVM* vm要这样 vm->方法名(参数列表)
上面这二者的区别是,在C中必须先对env和vm间接寻址(得到的内容仍然是一个指针),在调用方法时要将env或vm传入作为第一个参数。C++则直接利用env和vm指针调用其成员。
至于具体的原因请看下面这篇博客我觉得讲的比较好:
http://blog.csdn.net/freechao/article/details/7692239
总结篇:
JNI静态注册:
第一步:在MainActivity.java下面新建一个类XXX.java,形式如下:
public class addadd {
static {
System.loadLibrary("add");
}
public static native int addint(int a,int b);
}
第二步:使用javah命令生成以上的头文件Java_<包名>_<类名>_<方法名>.h的头文件;
第三步:在app目录下面实现更改build.gradle文件:
在defaultConfig{}里面增加:
ndk{
moduleName "add"
abiFilters "armeabi", "armeabi-v7a", "x86"
}
手动编译的具体步骤:
1.在jni目录下面新建Android.mk文件与eclipse下一样;
2.在build.gradle下面android{}里面加以下
sourceSets {
main {
jni.srcDirs = []
jniLibs.srcDirs = ['src/main/libs']
}}
3.在jni目录下进行ndk-build编译
第四步:在gradle.properties文件下面加一句:android.useDeprecatedNdk=true;
第五步:在MainActivity下面补充完整功能,调用本地层功能
***************************************************************************************************
JNI的动态注册:代码形式是固定的,见上面。
附件:
源码:点击打开链接
最后吐槽一下这个编辑器太特么难用了,每次都花好长时间在这上面!!!!!