INIT与init array
1.so执行JNI_OnLoad之前,还会执行俩个构造函数init 和init array
在so加载时候有这个过程:
.init -> .init array -> JNI_Onload -> java_com_xxx
在脱壳的过程中会在一些系统级的.so中下断点比如:fopen,fget,dvmdexfileopen等等
而 .init 以及 .init_array 一般会作为壳的入口地方,那我们索性叫它外壳级的.so文件
这里归纳为三类:
应用级别的:java_com_XXX;
外壳级别的:JNI_Onload,.init,.init_array;
系统级别的:fopen,fget,dvmdexfileopen;
对于在应用级别的和系统级别的就不说了比较简单容易理解,这里也是在实现篇中会重点说的,看到上面的.so的加载执行过程我们知道如果说反调试放在外壳级别的.so文件的话我们就会遇程序在应用级核心函数一下断点就退出的尴尬,事实上多数的反调试会放在这,那么过反调试就必须要在这些地方下断点,下面会重点的说如何在.init_array和JNI_Onload处理下断点。
2.so加固 so字符串加密等等,一般会把相关代码放到这里
3.init的使用
exter “C” void _init(){
//函数名必须是_init
……
}
在 JNI 中,INIT
通常指的是调用 Java 类的构造函数以创建 Java 对象。以下是关于 INIT
的详细说明及其使用示例。
使用 INIT
的步骤
- 查找 Java 类:
- 使用
FindClass
查找要实例化的 Java 类。
- 使用
- 获取构造函数 ID:
- 使用
GetMethodID
获取构造函数的 ID。
- 使用
- 创建构造函数参数:
- 创建构造函数所需的参数(如字符串、整数等)。
- 调用构造函数:
- 使用
NewObject
调用构造函数,创建 Java 对象。
- 使用
- 释放局部引用:
- 释放局部引用以避免内存泄漏。
示例代码
以下是一个完整的示例,展示了如何在 JNI 中使用 INIT
创建和初始化一个 Java 对象。
Java 类示例
假设我们有一个 Java 类 Person
,它有一个构造函数和一个方法:
package com.example;public class Person {private String name;public Person(String name) {this.name = name;}public String getName() {return name;}
}
JNI 代码示例
接下来,我们编写 JNI 代码来创建 Person
对象并调用其方法:
#include <jni.h>
#include <string>extern "C" JNIEXPORT void JNICALL
Java_com_example_yourapp_MainActivity_createPerson(JNIEnv *env, jobject obj) {// 1. 查找 Java 类jclass personClass = env->FindClass("com/example/Person");if (personClass == nullptr) {return; // 类未找到,返回}// 2. 获取构造函数 IDjmethodID constructor = env->GetMethodID(personClass, "<init>", "(Ljava/lang/String;)V");if (constructor == nullptr) {return; // 构造函数未找到,返回}// 3. 创建构造函数参数jstring name = env->NewStringUTF("John Doe");// 4. 创建 Java 对象jobject personObject = env->NewObject(personClass, constructor, name);if (personObject == nullptr) {env->DeleteLocalRef(name); // 释放局部引用return; // 对象创建失败,返回}// 5. 获取 getName 方法 IDjmethodID getNameMethod = env->GetMethodID(personClass, "getName", "()Ljava/lang/String;");if (getNameMethod == nullptr) {env->DeleteLocalRef(personObject);env->DeleteLocalRef(name);return; // 方法未找到,返回}// 6. 调用 getName 方法jstring result = (jstring)env->CallObjectMethod(personObject, getNameMethod);if (result != nullptr) {const char *resultStr = env->GetStringUTFChars(result, nullptr);// 这里可以使用 resultStr,例如打印或处理env->ReleaseStringUTFChars(result, resultStr);env->DeleteLocalRef(result); // 释放局部引用}// 7. 释放局部引用env->DeleteLocalRef(personObject);env->DeleteLocalRef(name);
}
代码解释
- 查找 Java 类:使用
FindClass
查找Person
类。 - 获取构造函数 ID:使用
GetMethodID
获取构造函数的 ID。 - 创建构造函数参数:创建一个字符串参数
name
。 - 调用构造函数:使用
NewObject
创建Person
对象。 - 获取方法 ID:获取
getName
方法的 ID。 - 调用方法:使用
CallObjectMethod
调用getName
方法,获取返回值。 - 释放局部引用:在适当的地方释放局部引用,以避免内存泄漏。
总结
- 使用
INIT
可以在 JNI 中创建 Java 对象。 - 通过
NewObject
调用构造函数,并使用GetMethodID
和CallObjectMethod
调用对象的方法。 - 确保在适当的地方释放局部引用,以避免内存泄漏。
4.initarray的使用
在 JNI 中,INITARRAY
通常指的是初始化 Java 对象数组的过程。使用 NewObjectArray
创建一个 Java 对象数组,并使用 SetObjectArrayElement
方法将对象添加到数组中。以下是关于 INITARRAY
的详细说明及其使用示例。
使用 INITARRAY
的步骤
- 查找 Java 类:
- 使用
FindClass
查找要存储在数组中的 Java 类。
- 使用
- 创建对象数组:
- 使用
NewObjectArray
创建一个指定长度的对象数组。
- 使用
- 获取构造函数 ID:
- 获取要创建的 Java 对象的构造函数 ID。
- 初始化数组元素:
- 使用循环创建每个元素,并使用
SetObjectArrayElement
将其添加到数组中。
- 使用循环创建每个元素,并使用
- 释放局部引用:
- 释放局部引用以避免内存泄漏。
示例代码
以下是一个完整的示例,展示了如何在 JNI 中使用 INITARRAY
创建和初始化一个 Java 对象数组。
Java 类示例
首先,假设我们有一个 Java 类 MyClass
,它有一个构造函数:
package com.example;public class MyClass {private String name;public MyClass(String name) {this.name = name;}public String getName() {return name;}
}
JNI 代码示例
接下来,我们编写 JNI 代码来创建和初始化 MyClass
对象数组:
#include <jni.h>
#include <string>extern "C" JNIEXPORT jobjectArray JNICALL
Java_com_example_yourapp_MainActivity_createMyClassArray(JNIEnv *env, jobject obj) {// 1. 查找 Java 类jclass myClass = env->FindClass("com/example/MyClass");if (myClass == nullptr) {return nullptr; // 类未找到,返回 nullptr}// 2. 获取构造函数 IDjmethodID constructor = env->GetMethodID(myClass, "<init>", "(Ljava/lang/String;)V");if (constructor == nullptr) {return nullptr; // 构造函数未找到,返回 nullptr}// 3. 创建对象数组jobjectArray myArray = env->NewObjectArray(5, myClass, nullptr);if (myArray == nullptr) {return nullptr; // 数组创建失败,返回 nullptr}// 4. 初始化数组元素for (int i = 0; i < 5; i++) {// 创建构造函数参数std::string name = "Object " + std::to_string(i);jstring jName = env->NewStringUTF(name.c_str());// 5. 创建 Java 对象jobject myObject = env->NewObject(myClass, constructor, jName);if (myObject == nullptr) {env->DeleteLocalRef(jName); // 释放局部引用return nullptr; // 对象创建失败,返回 nullptr}// 6. 将对象添加到数组env->SetObjectArrayElement(myArray, i, myObject);// 7. 释放局部引用env->DeleteLocalRef(myObject);env->DeleteLocalRef(jName);}// 8. 返回对象数组return myArray;
}
代码解释
- 查找 Java 类:使用
FindClass
查找MyClass
。 - 获取构造函数 ID:使用
GetMethodID
获取构造函数的 ID。 - 创建对象数组:使用
NewObjectArray
创建一个长度为 5 的对象数组。 - 初始化数组元素:使用循环创建每个
MyClass
对象,将其添加到数组中。- 创建字符串参数并调用构造函数。
- 使用
SetObjectArrayElement
将创建的对象添加到数组中。
- 释放局部引用:在每次循环中释放局部引用,以避免内存泄漏。
- 返回对象数组:最后返回创建的对象数组。
总结
- 使用
INITARRAY
可以在 JNI 中创建和初始化 Java 对象数组。 - 通过
NewObjectArray
创建数组,并使用SetObjectArrayElement
方法将元素添加到数组中。 - 确保在适当的地方释放局部引用,以避免内存泄漏。
这种方式可以有效地在 C++ 和 Java 之间传递对象数组,方便在 Android 应用中实现复杂的数据结构。
5.onCreateNative化
在 Android 应用中,onCreate
方法是 Activity 生命周期中的一个重要部分。将 onCreate
方法的部分逻辑移到 JNI 中,可以通过调用本地方法来实现。以下是如何将 onCreate
方法的逻辑“本地化”的步骤和示例。
步骤
- 定义本地方法:在 Java 类中声明一个本地方法。
- 实现本地方法:在 C++ 中实现该本地方法。
- 在
onCreate
中调用本地方法:在onCreate
方法中调用本地方法。
示例代码
1. Java 类定义
首先,在你的 Activity 中定义一个本地方法:
package com.example.yourapp;import android.app.Activity;
import android.os.Bundle;public class MainActivity extends Activity {static {System.loadLibrary("native-lib"); // 加载本地库}// 声明本地方法public native void onCreateNative();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 调用本地方法onCreateNative();}
}
2. C++ 实现
接下来,在 C++ 中实现 onCreateNative
方法:
#include <jni.h>
#include <string>
#include <android/log.h>#define LOG_TAG "NativeLib"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)extern "C" JNIEXPORT void JNICALL
Java_com_example_yourapp_MainActivity_onCreateNative(JNIEnv *env, jobject obj) {// 在这里实现本地逻辑LOGI("onCreateNative called");// 你可以在这里执行其他初始化操作// 例如,初始化 C++ 对象、设置 JNI 环境等
}
代码解释
- Java 类:
- 在
MainActivity
中声明了一个本地方法onCreateNative
。 - 在
onCreate
方法中调用onCreateNative
,以便在 Activity 创建时执行本地逻辑。
- 在
- C++ 实现:
- 使用
JNIEXPORT
和JNICALL
定义本地方法。 - 使用
__android_log_print
打印日志,确认本地方法被调用。
- 使用