美文网首页
android 从jobject、jclass获取类名(API

android 从jobject、jclass获取类名(API

作者: 处于蒙比阶段的小白 | 来源:发表于2018-10-30 00:04 被阅读0次

前言

用途在Art中Hook JNI相关函数。存在jobject jclass 参数时需要得到具体的类名。

在Art虚拟机中:

jobject在内存中表现为:art::mirror::Object,可从GetObjectClass方法中分析得到(art/runtime/jni_internal.cc)


  static jclass GetObjectClass(JNIEnv* env, jobject java_object) {

    CHECK_NON_NULL_ARGUMENT(java_object);

    ScopedObjectAccess soa(env);

    ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(java_object);

    return soa.AddLocalReference<jclass>(o->GetClass());

  }

jclass在内存中表现为:art::mirror::Class,可从GetSuperclass方法中分析得到(art/runtime/jni_internal.cc)


  static jclass GetSuperclass(JNIEnv* env, jclass java_class) {

    CHECK_NON_NULL_ARGUMENT(java_class);

    ScopedObjectAccess soa(env);

    ObjPtr<mirror::Class> c = soa.Decode<mirror::Class>(java_class);

    return soa.AddLocalReference<jclass>(c->IsInterface() ? nullptr : c->GetSuperClass());

  }

正文

获取类名重点在art::mirror::Class类中,通过分析Class 类发现在art/runtime/mirror/class-inl.h头文件中存在一个获取类名的方法


inline String* Class::GetName() {

  return GetFieldObject<String>(OFFSET_OF_OBJECT_MEMBER(Class, name_));

}

上述方法存在两个问题:

  • inline内联函数,编译后在libart.so不存在导出符号

  • 返回值String类型是art虚拟机一个内部类,调用起来太麻烦

综上所述,GetName不适合获取类名

在dalvik虚拟机中存在一个方法dvmDecodeIndirectRef,可以将jobject、jclass转为对应的内存结构指针。

经过查找发现在/art/runtime/thread.cc中存在一个方法DecodeJObject可以将jobject、jclass转换为对应的内存结构指针


ObjPtr<mirror::Object> Thread::DecodeJObject(jobject obj) const {

    ..............

}

DecodeJObject是Thread类的一个方法,通过dlsym拿到方法地址后,其表现形式如下:


void *(*DecodeJObject)(void *thisobj, jobject jobject);

第一个参数表示this即当前对象。可以通过如下方法获取Thread对象的实例


void *Art::Current() {

#if defined(__aarch64__)

# define __get_tls() ({ void** __val; __asm__("mrs %0, tpidr_el0" : "=r"(__val)); __val; })

#elif defined(__arm__)

# define __get_tls() ({ void** __val; __asm__("mrc p15, 0, %0, c13, c0, 3" : "=r"(__val)); __val; })

#elif defined(__mips__)

# define __get_tls() \

    /* On mips32r1, this goes via a kernel illegal instruction trap that's optimized for v1. */ \

    ({ register void** __val asm("v1"); \

       __asm__(".set push\n" \

               ".set mips32r2\n" \

               "rdhwr %0,$29\n" \

               ".set pop\n" : "=r"(__val)); \

       __val; })

#elif defined(__i386__)

# define __get_tls() ({ void** __val; __asm__("movl %%gs:0, %0" : "=r"(__val)); __val; })

#elif defined(__x86_64__)

# define __get_tls() ({ void** __val; __asm__("mov %%fs:0, %0" : "=r"(__val)); __val; })

#else

#error unsupported architecture

#endif

    enum {

        TLS_SLOT_SELF = 0,

        // The kernel requires this specific slot for x86.

                TLS_SLOT_THREAD_ID,

        TLS_SLOT_ERRNO,



        // These two aren't used by bionic itself, but allow the graphics code to

        // access TLS directly rather than using the pthread API.

                TLS_SLOT_OPENGL_API = 3,

        TLS_SLOT_OPENGL = 4,



        // This slot is only used to pass information from the dynamic linker to

        // libc.so when the C library is loaded in to memory. The C runtime init

        // function will then clear it. Since its use is extremely temporary,

        // we reuse an existing location that isn't needed during libc startup.

                TLS_SLOT_BIONIC_PREINIT = TLS_SLOT_OPENGL_API,



        TLS_SLOT_STACK_GUARD = 5,

        // GCC requires this specific slot for x86.

                TLS_SLOT_DLERROR,



        // Fast storage for Thread::Current() in ART.

                TLS_SLOT_ART_THREAD_SELF,



        // Lets TSAN avoid using pthread_getspecific for finding the current thread

        // state.

                TLS_SLOT_TSAN,



        BIONIC_TLS_SLOTS // Must come last!

    };

    if (sdkVersion >= Nougat) {

        //get thread form asm code

        void *thread = __get_tls()[TLS_SLOT_ART_THREAD_SELF];

        return thread;

    } else {

        if (pthread_key_self_handle == nullptr) {

            pthread_key_self_handle = fake_dlsym(libartHandle, pthread_key_self_Symbol.c_str());

        }

        pthread_key_t key = *(pthread_key_t *) pthread_key_self_handle;

        void *thread = pthread_getspecific(key);

        return thread;

    }

}

同时发现/art/runtime/mirror/class.cc中存在GetDescriptor方法可以获取art::mirror::Class的类名


const char* Class::GetDescriptor(std::string* storage) {

  if (IsPrimitive()) {

    return Primitive::Descriptor(GetPrimitiveType());

  } else if (IsArrayClass()) {

    return GetArrayDescriptor(storage);

  } else if (IsProxyClass()) {

    *storage = Runtime::Current()->GetClassLinker()->GetDescriptorForProxy(this);

    return storage->c_str();

  } else {

    const DexFile& dex_file = GetDexFile();

    const DexFile::TypeId& type_id = dex_file.GetTypeId(GetClassDef()->class_idx_);

    return dex_file.GetTypeDescriptor(type_id);

  }

}

GetDescriptor是Class类的一个方法,通过dlsym拿到方法地址后,其表现形式如下:


 const char *(*getClassDescriptor)(void *thisobj, void *temp);

第一个参数表示this即当前对象。可以通过DecodeJObject获取Class对象的实例,

在最终可以整理得到获取jclass类名的方法:


const char *Art::getClasstName(jclass clazz) {

    void *pVoid = DecodeJObject(Current(), clazz);

    std::string tmp;

    const char *data = getClassDescriptor(pVoid, &tmp);

    return data;

}

获取jobject方法就简单一点其内存结构可以精简如下:


class ARTObject {

public:

    uint32_t klass_;

    uint32_t monitor_;

};

其中klass就是Class对象的指针,最终整理后的方法如下:


const char *Art::getObjectName(jobject obj) {

    void *k_class = nullptr;

    void *pVoid = DecodeJObject(Current(), obj);

    ARTObject *object_api21 = (ARTObject *) (pVoid);

    k_class = (void *) object_api21->klass_;

    std::string tmp;

    const char *data = getClassDescriptor(k_class, &tmp);

    return data;

    }

上述方法太过于麻烦,后面给出一个简单的方法,可以模拟dalvik解析dex拿到类名。不早了,睡了

相关文章

网友评论

      本文标题:android 从jobject、jclass获取类名(API

      本文链接:https://www.haomeiwen.com/subject/sohwtqtx.html