You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JNI调用Class.getDeclaredFields()报错及Android代码保护实现问询

JNI调用Class.getDeclaredFields()报错问题解决

问题描述

  • 要实现Android代码保护,Java反射版本的CREATOR回滚功能正常,但JNI/C++版本运行报错
  • 核心需求:在JNI中正确调用java.lang.Class.getDeclaredFields()
  • 错误信息:JNI DETECTED ERROR IN APPLICATION: can't call java.lang.reflect.Field[] java.lang.Class.getDeclaredFields() on instance of com.metadata.MetaData$1
  • 疑问:该问题是代码错误还是Android平台限制?

相关代码示例

1. 目标类MetaData

package com.metadata;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;

class MetaData implements Parcelable {
   public String data;

   private MetaData(Parcel in) {
      data = in.readString();
   }

   @Override
   public int describeContents() {
      return 0;
   }

   @Override
   public void writeToParcel(@NonNull Parcel dest, int flags) {
      dest.writeString(data);
   }

   public static final Creator<MetaData> CREATOR = new Creator<MetaData>() {
      @Override
      public MetaData createFromParcel(Parcel in) {
         return new MetaData(in);
      }

      @Override
      public MetaData[] newArray(int size) {
         return new MetaData[size];
      }
   };
}

2. 篡改CREATOR的攻击代码

public static void modify(String newData) {
        MetaData.Creator<MetaData> original = MetaData.CREATOR;
        MetaData.Creator<MetaData> fake = new Parcelable.Creator<MetaData>() {
            @Override
            public MetaData createFromParcel(Parcel source) {
                MetaData metaData = original.createFromParcel(source);
                metaData.data = newData;
                return metaData;
            }

            @Override
            public MetaData[] newArray(int size) {
                return original.newArray(size);
            }
        };

        try {
            Field field = MetaData.class.getDeclaredField("CREATOR");
            field.setAccessible(true);
            field.set(null, fake);
        } catch (Throwable e) {
            // ignore
        }
    }

3. Java版本的CREATOR回滚代码

public static void rollbackModification() {
        try {
            MetaData.Creator<MetaData> metaDataCreator = MetaData.CREATOR;
            Class<?> metaDataCreatorClass = metaDataCreator.getClass();
            Field[] fields = metaDataCreatorClass.getDeclaredFields();
            if (fields == null || fields.length == 0) return;
            Object originalCreator = null;
            for (Field field : fields) {
                // 查找原始CREATOR
                Class clazz = field.getType();
                if (clazz == Parcelable.Creator.class) {
                    field.setAccessible(true);
                    originalCreator = field.get(metaDataCreator);
                    break;
                }
            }
            if (originalCreator != null) {
                Field field = MetaData.class.getDeclaredField("CREATOR");
                field.setAccessible(true);;
                field.set(null, originalCreator);
            }
        } catch (Throwable e) {
            // ignore
        }
    }

4. 报错的JNI/C++代码

#include <string.h>
#include <jni.h>

#include <android/log.h>

// Android日志封装
static const char* kTAG = "native_log-jniCallback";
#define LOGI(...) \
  ((void)__android_log_print(ANDROID_LOG_INFO, kTAG, __VA_ARGS__))
#define LOGW(...) \
  ((void)__android_log_print(ANDROID_LOG_WARN, kTAG, __VA_ARGS__))
#define LOGE(...) \
  ((void)__android_log_print(ANDROID_LOG_ERROR, kTAG, __VA_ARGS__))

extern "C" {

Java_com_metadata_Test_test(JNIEnv *env, jobject obj, jobject cnt) {
    rollbackModification(env, obj, cnt);
}

rollbackModification(JNIEnv *env, jobject obj, jobject cnt) {
    try {
 
    jclass metaDataClass = env->FindClass("com/metadata/MetaData");
    jclass parcelableCreatorClass = env->FindClass("android/os/Parcelable$Creator");

    // 获取MetaData.CREATOR
    jfieldID fakeCreatorField = env->GetStaticFieldID(metaDataClass, "CREATOR", "Landroid/os/Parcelable$Creator;");
    jobject metaDataCreator = env->GetStaticObjectField(metaDataClass, fakeCreatorField);

    // 检查MetaData.CREATOR的字段
    if (metaDataCreator != NULL) {

    jclass fakeMetaDataCreatorClass = env->GetObjectClass(metaDataCreator);

    jmethodID fakeMetaDataCreatorClassMethod = env->GetMethodID(fakeMetaDataCreatorClass, "getClass", "()Ljava/lang/Class;");
    jobject fakeMetaDataCreatorClassObject = env->CallObjectMethod(fakeMetaDataCreatorClass, fakeMetaDataCreatorClassMethod);
    jclass fakeMetaDataCreatorClassObjectClass = env->GetObjectClass(fakeMetaDataCreatorClassObject);

    // 尝试获取字段,此处报错
    jmethodID declaredFieldsMethod = env->GetMethodID(fakeMetaDataCreatorClassObjectClass, "getDeclaredFields", "()[Ljava/lang/reflect/Field;");
    jobjectArray fields = (jobjectArray)env->CallObjectMethod(metaDataCreator, declaredFieldsMethod);

    jsize numFields = env->GetArrayLength(fields);
    jobject originalCreator = NULL;

    // 查找原始字段
    for (jsize i = 0; i < numFields; i++) {
            jobject field = env->GetObjectArrayElement(fields, i);
            jclass fieldClass = env->GetObjectClass(field);
            jmethodID getTypeMethod = env->GetMethodID(fieldClass, "getType", "()Ljava/lang/Class;");
            jobject fieldType = env->CallObjectMethod(field, getTypeMethod);
            // 查找原始CREATOR对象
            if (env->IsSameObject(fieldType, parcelableCreatorClass)) {
                jmethodID setAccessibleMethod = env->GetMethodID(fieldClass, "setAccessible", "(Z)V");
                env->CallVoidMethod(field, setAccessibleMethod, JNI_TRUE);

                jmethodID getMethod = env->GetMethodID(fieldClass, "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
                originalCreator = env->CallObjectMethod(field, getMethod, metaDataCreator);
                break;
            }
        }
        // 将MetaData.CREATOR从伪造对象换回原始对象
        if (originalCreator != NULL) {
            jmethodID setMethod = env->GetMethodID(fakeMetaDataCreatorClass, "set", "(Ljava/lang/Object;Ljava/lang/Object;)V");
            env->CallVoidMethod(metaDataCreator, setMethod, NULL, originalCreator);
        }
    }

   } catch (...){ }
}
}

问题原因分析

报错的核心是调用getDeclaredFields时用错了对象:

  • getDeclaredFields()是java.lang.Class类的方法,必须调用在Class实例上
  • 原JNI代码中,错误地调用在了metaDataCreator(MetaData$1对象实例)上,而非fakeMetaDataCreatorClassObject(Class实例)
  • 另外最后替换CREATOR时,错误地调用了metaDataCreator的set方法,实际应该调用Field对象的set方法

修正后的JNI代码

#include <string.h>
#include <jni.h>

#include <android/log.h>

// Android日志封装
static const char* kTAG = "native_log-jniCallback";
#define LOGI(...) \
  ((void)__android_log_print(ANDROID_LOG_INFO, kTAG, __VA_ARGS__))
#define LOGW(...) \
  ((void)__android_log_print(ANDROID_LOG_WARN, kTAG, __VA_ARGS__))
#define LOGE(...) \
  ((void)__android_log_print(ANDROID_LOG_ERROR, kTAG, __VA_ARGS__))

extern "C" {

void Java_com_metadata_Test_test(JNIEnv *env, jobject obj, jobject cnt) {
    rollbackModification(env, obj, cnt);
}

void rollbackModification(JNIEnv *env, jobject obj, jobject cnt) {
    jclass metaDataClass = env->FindClass("com/metadata/MetaData");
    if (metaDataClass == nullptr) {
        LOGE("Failed to find MetaData class");
        return;
    }

    jclass parcelableCreatorClass = env->FindClass("android/os/Parcelable$Creator");
    if (parcelableCreatorClass == nullptr) {
        LOGE("Failed to find Parcelable.Creator class");
        return;
    }

    // 获取MetaData.CREATOR静态字段
    jfieldID creatorFieldId = env->GetStaticFieldID(metaDataClass, "CREATOR", "Landroid/os/Parcelable$Creator;");
    if (creatorFieldId == nullptr) {
        LOGE("Failed to get CREATOR field ID");
        return;
    }
    jobject currentCreator = env->GetStaticObjectField(metaDataClass, creatorFieldId);
    if (currentCreator == nullptr) {
        LOGE("Current CREATOR is null");
        return;
    }

    // 获取currentCreator的Class实例
    jclass objectClass = env->FindClass("java/lang/Object");
    jmethodID getClassMethodId = env->GetMethodID(objectClass, "getClass", "()Ljava/lang/Class;");
    jobject creatorClazz = env->CallObjectMethod(currentCreator, getClassMethodId);
    if (creatorClazz == nullptr) {
        LOGE("Failed to get creator class");
        return;
    }

    // 获取Class.getDeclaredFields()方法
    jclass classClass = env->FindClass("java/lang/Class");
    jmethodID getDeclaredFieldsMethodId = env->GetMethodID(classClass, "getDeclaredFields", "()[Ljava/lang/reflect/Field;");
    jobjectArray fields = (jobjectArray)env->CallObjectMethod(creatorClazz, getDeclaredFieldsMethodId);
    if (fields == nullptr) {
        LOGE("Failed to get declared fields");
        return;
    }

    jsize fieldCount = env->GetArrayLength(fields);
    jobject originalCreator = nullptr;

    // 遍历字段查找原始Creator
    for (jsize i = 0; i < fieldCount; i++) {
        jobject field = env->GetObjectArrayElement(fields, i);
        jclass fieldClass = env->FindClass("java/lang/reflect/Field");

        // 获取字段类型
        jmethodID getTypeMethodId = env->GetMethodID(fieldClass, "getType", "()Ljava/lang/Class;");
        jobject fieldType = env->CallObjectMethod(field, getTypeMethodId);
        if (env->IsSameObject(fieldType, parcelableCreatorClass)) {
            // 设置字段可访问
            jmethodID setAccessibleMethodId = env->GetMethodID(fieldClass, "setAccessible", "(Z)V");
            env->CallVoidMethod(field, setAccessibleMethodId, JNI_TRUE);

            // 获取字段值(原始Creator)
            jmethodID getMethodId = env->GetMethodID(fieldClass, "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
            originalCreator = env->CallObjectMethod(field, getMethodId, currentCreator);
            break;
        }
        env->DeleteLocalRef(field);
    }

    // 替换回原始Creator
    if (originalCreator != nullptr) {
        jclass fieldClass = env->FindClass("java/lang/reflect/Field");
        jmethodID setMethodId = env->GetMethodID(fieldClass, "set", "(Ljava/lang/Object;Ljava/lang/Object;)V");
        jmethodID setAccessibleMethodId = env->GetMethodID(fieldClass, "setAccessible", "(Z)V");
        
        // 获取MetaData.CREATOR字段对象
        jmethodID getDeclaredFieldMethodId = env->GetMethodID(classClass, "getDeclaredField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;");
        jobject creatorField = env->CallObjectMethod(metaDataClass, getDeclaredFieldMethodId, env->NewStringUTF("CREATOR"));
        
        env->CallVoidMethod(creatorField, setAccessibleMethodId, JNI_TRUE);
        env->CallVoidMethod(creatorField, setMethodId, nullptr, originalCreator);
        
        env->DeleteLocalRef(creatorField);
    }

    // 释放局部引用
    env->DeleteLocalRef(metaDataClass);
    env->DeleteLocalRef(parcelableCreatorClass);
    env->DeleteLocalRef(objectClass);
    env->DeleteLocalRef(creatorClazz);
    env->DeleteLocalRef(classClass);
    env->DeleteLocalRef(fields);
    env->DeleteLocalRef(currentCreator);
    if (originalCreator != nullptr) {
        env->DeleteLocalRef(originalCreator);
    }
}
}

补充说明

  • 该问题属于代码逻辑错误,并非Android非SDK接口限制。只要正确调用Class类的方法,就能正常获取字段
  • 注意在JNI中及时释放局部引用,避免内存泄漏
  • 针对Android高版本的非SDK接口限制,若后续遇到相关问题,需确认调用的方法是否在允许列表内

内容的提问来源于stack exchange,提问作者Prilaga

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 13:47:04