Java
在Android开发一个Activity,常规Java写法如下:
package com.zyc.oncreate2native;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView txt = findViewById(R.id.txt_main);
txt.setText("onCreate-->Native success!");
}
}
运行:
这样的写法放进反编译工具,分析起来几乎没有难度。
Native
上面的代码Native化之后:
#include <jni.h>
#include <string>
#include <android/log.h>
extern "C" JNIEXPORT void JNICALL
Java_com_zyc_oncreate2native_MainActivity_onCreate(JNIEnv *env, jobject thiz, jobject saved_instance_state) {
//super.onCreate(savedInstanceState);
jclass MainActivity = env->GetObjectClass(thiz);
jclass AppCompatActivity = env->GetSuperclass(MainActivity);
jmethodID onCreate = env->GetMethodID(AppCompatActivity, "onCreate", "(Landroid/os/Bundle;)V");
env->CallNonvirtualVoidMethod(thiz, AppCompatActivity, onCreate, saved_instance_state); //调用父类方法
//setContentView(R.layout.activity_main);
jmethodID setContentView = env->GetMethodID(MainActivity, "setContentView", "(I)V");
jclass Layout = env->FindClass("com/zyc/oncreate2native/R$layout"); //注意R类的写法
jfieldID activity_main = env->GetStaticFieldID(Layout, "activity_main", "I");
jint id_activity_main = env->GetStaticIntField(Layout, activity_main);
env->CallVoidMethod(thiz, setContentView, id_activity_main);
//TextView txt = findViewById(R.id.txt_main);
jmethodID findViewById = env->GetMethodID(MainActivity, "findViewById","(I)Landroid/view/View;");
jclass id = env->FindClass("com/zyc/oncreate2native/R$id");
jfieldID txt_main = env->GetStaticFieldID(id, "txt_main", "I");
jint id_txt_main = env->GetStaticIntField(id, txt_main);
jobject txt = env->CallObjectMethod(thiz, findViewById, id_txt_main);
//txt.setText("onCreate-->Native success by c++ !!!");
jclass TextView = env->FindClass("android/widget/TextView");
jmethodID setText = env->GetMethodID(TextView, "setText", "(Ljava/lang/CharSequence;)V");
jstring text = env->NewStringUTF("onCreate-->Native success by c++ !!!");
env->CallVoidMethod(txt, setText, text);
env->DeleteLocalRef(text);
}
运行:
这时再反编译,Java层就无法看到有用信息了。
即使找到so文件分析伪C++代码,也头疼多了。
int __cdecl Java_com_zyc_oncreate2native_MainActivity_onCreate(_JNIEnv *a1, int a2, int a3)
{
int v3; // eax
int v4; // ST6C_4
int v5; // eax
int v6; // ST68_4
int v7; // eax
int v8; // ST60_4
int v9; // eax
int v10; // ST5C_4
int v11; // eax
char v12; // al
int v13; // ST50_4
int v14; // eax
int v15; // ST4C_4
int v16; // eax
char v17; // al
int v18; // ST40_4
int v19; // eax
int v20; // ST38_4
int v21; // ST34_4
v3 = _JNIEnv::GetObjectClass(a1, a2);
v4 = v3;
v5 = _JNIEnv::GetSuperclass(a1, v3);
v6 = v5;
v7 = _JNIEnv::GetMethodID(a1, v5, 4564, 4573);
_JNIEnv::CallNonvirtualVoidMethod(a1, a2, v6, v7, a3);
v8 = _JNIEnv::GetMethodID(a1, v4, "setContentView", "(I)V");
v9 = _JNIEnv::FindClass(a1, "com/zyc/oncreate2native/R$layout");
v10 = v9;
v11 = _JNIEnv::GetStaticFieldID(a1, v9, 4649, 4663);
v12 = _JNIEnv::GetStaticIntField(a1, v10, v11);
_JNIEnv::CallVoidMethod(a1, a2, v8, v12);
v13 = _JNIEnv::GetMethodID(a1, v4, "findViewById", "(I)Landroid/view/View;");
v14 = _JNIEnv::FindClass(a1, "com/zyc/oncreate2native/R$id");
v15 = v14;
v16 = _JNIEnv::GetStaticFieldID(a1, v14, 4730, 4663);
v17 = _JNIEnv::GetStaticIntField(a1, v15, v16);
v18 = _JNIEnv::CallObjectMethod(a1, a2, v13, v17);
v19 = _JNIEnv::FindClass(a1, "android/widget/TextView");
v20 = _JNIEnv::GetMethodID(a1, v19, 4763, 4771);
v21 = _JNIEnv::NewStringUTF(a1, "onCreate-->Native success by c++ !!!");
_JNIEnv::CallVoidMethod(a1, v18, v20, v21);
return _JNIEnv::DeleteLocalRef(a1, v21);
}
当然, Java_com_zyc_oncreate2native_MainActivity_onCreate() 的方法名还是容易暴露,换成改成动态注册:
#include <jni.h>
#include <string>
#include <android/log.h>
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "Tag", __VA_ARGS__)
void ffff(JNIEnv *env, jobject thiz, jobject saved_instance_state) {
//super.onCreate(savedInstanceState);
jclass MainActivity = env->GetObjectClass(thiz);
jclass AppCompatActivity = env->GetSuperclass(MainActivity);
jmethodID onCreate = env->GetMethodID(AppCompatActivity, "onCreate", "(Landroid/os/Bundle;)V");
env->CallNonvirtualVoidMethod(thiz, AppCompatActivity, onCreate, saved_instance_state); //调用父类方法
//setContentView(R.layout.activity_main);
jmethodID setContentView = env->GetMethodID(MainActivity, "setContentView", "(I)V");
jclass Layout = env->FindClass("com/zyc/oncreate2native/R$layout");
jfieldID activity_main = env->GetStaticFieldID(Layout, "activity_main", "I");
jint id_activity_main = env->GetStaticIntField(Layout, activity_main);
env->CallVoidMethod(thiz, setContentView, id_activity_main);
//TextView txt = findViewById(R.id.txt_main);
jmethodID findViewById = env->GetMethodID(MainActivity, "findViewById",
"(I)Landroid/view/View;");
jclass id = env->FindClass("com/zyc/oncreate2native/R$id");
jfieldID txt_main = env->GetStaticFieldID(id, "txt_main", "I");
jint id_txt_main = env->GetStaticIntField(id, txt_main);
jobject txt = env->CallObjectMethod(thiz, findViewById, id_txt_main);
//txt.setText("onCreate-->Native success by c++ !!!");
jclass TextView = env->FindClass("android/widget/TextView");
jmethodID setText = env->GetMethodID(TextView, "setText", "(Ljava/lang/CharSequence;)V");
jstring text = env->NewStringUTF("onCreate-->Native success by c++ ffff!!!");
env->CallVoidMethod(txt, setText, text);
env->DeleteLocalRef(text);
}
static JNINativeMethod methods[] = {
{"onCreate", "(Landroid/os/Bundle;)V", (void*)ffff}
};
static int registerNatives(JNIEnv *env) {
//找到声明native方法的类
const char *className = "com/zyc/oncreate2native/MainActivity";
jclass clazz = env->FindClass(className);
if (clazz == NULL) {
return JNI_FALSE;
}
//注册函数 参数:java类 所要注册的函数数组 注册函数的个数
int methodsNum = sizeof(methods) / sizeof(methods[0]);
if (env->RegisterNatives(clazz, methods, methodsNum) < 0) {
return JNI_FALSE;
}
return JNI_TRUE;
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
//获取JNIEnv
if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
assert(env != NULL);
if (!registerNatives(env)) {
return -1;
}
//返回jni 的版本
return JNI_VERSION_1_6;
}
这样一来定位 onCreate() 就更加困难了。
总 结
由上面不难看出, onCreate() 函数在Native化后逆向分析起来就困难多了。同理,其他Java函数也可以如法炮制,当然这一方法在提高安全性的同时也降低了开发效率。