前言
用途在Art中Hook JNI相關(guān)函數(shù)。存在jobject jclass 參數(shù)時需要得到具體的類名。
在Art虛擬機中:
jobject在內(nèi)存中表現(xiàn)為: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在內(nèi)存中表現(xiàn)為: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 類發(fā)現(xiàn)在art/runtime/mirror/class-inl.h頭文件中存在一個獲取類名的方法
inline String* Class::GetName() {
return GetFieldObject<String>(OFFSET_OF_OBJECT_MEMBER(Class, name_));
}
上述方法存在兩個問題:
inline內(nèi)聯(lián)函數(shù),編譯后在libart.so不存在導(dǎo)出符號
返回值String類型是art虛擬機一個內(nèi)部類,調(diào)用起來太麻煩
綜上所述,GetName不適合獲取類名
在dalvik虛擬機中存在一個方法dvmDecodeIndirectRef,可以將jobject、jclass轉(zhuǎn)為對應(yīng)的內(nèi)存結(jié)構(gòu)指針。
經(jīng)過查找發(fā)現(xiàn)在/art/runtime/thread.cc中存在一個方法DecodeJObject可以將jobject、jclass轉(zhuǎn)換為對應(yīng)的內(nèi)存結(jié)構(gòu)指針
ObjPtr<mirror::Object> Thread::DecodeJObject(jobject obj) const {
..............
}
DecodeJObject是Thread類的一個方法,通過dlsym拿到方法地址后,其表現(xiàn)形式如下:
void *(*DecodeJObject)(void *thisobj, jobject jobject);
第一個參數(shù)表示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;
}
}
同時發(fā)現(xiàn)/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拿到方法地址后,其表現(xiàn)形式如下:
const char *(*getClassDescriptor)(void *thisobj, void *temp);
第一個參數(shù)表示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方法就簡單一點其內(nèi)存結(jié)構(gòu)可以精簡如下:
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拿到類名。不早了,睡了