初识Jni。

好久没有搞过Android相关的内容,都差不多忘了,整理一下。

Android程序有两层,分为Java层和Native层,Java层就是Java代码编译为dex文件的,Native层是C++代码编译为so文件。

Android Studio原生支持jni,需要做以下修改。
1)修改gradle/wrapper/gradle-wrapper.properties文件中的distributionUrl字段为https\://services.gradle.org/distributions/gradle-2.10-all.zip,如果默认就是这样无需修改。
2)修改build.gradle为

1
2
3
4
5
6
7
8
9
10
11
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle-experimental:0.7.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}

3)修改build.gradle为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
apply plugin: 'com.android.model.application'
model {
android {
compileSdkVersion 23
buildToolsVersion "20.0.0"
defaultConfig {
applicationId "com.example.kernel32.checkdebug"
minSdkVersion.apiLevel 10
targetSdkVersion.apiLevel 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
// proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
proguardFiles.add(file("proguard-rules.pro"))
}
}
ndk{
moduleName "libTest"
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.+'
}

至此配置完毕。

静态注册

Java层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.example.jni;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn = (Button) findViewById(R.id.button);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(getApplicationContext(),HelloJni(),Toast.LENGTH_LONG).show();
}
});
}
public static native String HelloJni();
static{
System.loadLibrary("libTest");
}
}

Jni层

1
2
3
4
5
6
7
8
9
#include <jni.h>
JNIEXPORT jstring JNICALL
Java_com_example_jni_MainActivity_HelloJni(JNIEnv *env, jclass type) {
// TODO
return (*env)->NewStringUTF(env, "Hello Jni");
}

动态注册

Java层代码不变

Jni层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include "JniTest.h"
#include <jni.h>
#define NULL 0
jstring HelloJni(JNIEnv *env, jobject obj)
{
return env->NewStringUTF("HelloJni ");
}
static const char* gClassName = "com/example/jni/MainActivity"; //指定要注册的类
static JNINativeMethod gMethods[] = {
{"HelloJni", "()Ljava/lang/String;", (void*)HelloJni},
}; //方法对应表
static int registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod *gMethods, int numMethods) {
jclass clazz;
clazz = env->FindClass(className);
if(clazz == NULL) {
return JNI_FALSE;
}
if(env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
return JNI_FALSE;
}
return JNI_TRUE;
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void* reserved) {
JNIEnv* env = NULL;
jint result = -1;
if(vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
if (registerNativeMethods(env, gClassName, gMethods,
sizeof(gMethods) / sizeof(gMethods[0])) == JNI_FALSE)
{
return -1;
}
return JNI_VERSION_1_6;
}

用这种JNI Onload的方法,把Onload函数的在导出表里面抹掉,在动态初始化导出表,会大大增加逆向静态分析的成本。

一些小细节
Native函数声明:
如函数 public static HelloJni();
Java层声明为 public static native String HelloJni();
Native层声明为JNIEXPORT jstring JNICALL
Java_com_example_jni_MainActivity_HelloJni(JNIEnv *env, jclass type)

静态注册方法:
Native层则为 JNIEXPORT jstring JNICALL Java_com_example_jni_MainActivity_HelloJni(JNIENV env, jclass type)
So中的名字为 类名 + 函数名的组合,并且自带两个参数,JNIENV
env和
jclass (static 方法时)/jobject(普通方法时)。