android 从jobject、jclass获取类名(API>19)(一)

前言

用途在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拿到类名。不早了,睡了

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351