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