如何获取Java层的函数在Dalvik中对应的Method?
2016-01-02 12:57
441 查看
为什么要获取Java层的函数在Dalvik中对应的Method数据结构呢?
因为要Hook Java层的某个函数,首先得获取该函数在Dalvik中对应的Method数据结构,然后修改其函数指针指向另外一个函数,从而达到Hook的效果。
首先提出一个猜想,Java层通过反射可以获取函数的Method,而这个Method是否和Dalvik中的Method是一回事呢?
新建一个工程来验证一下(工程下载地址)
分别通过三种方式来获取Method:通过反射、通过slot、通过函数签名。
首先新建一个类Test.class,如下
Native层的实现如下:
MainActivity中调用如下:
运行后日志如下:
从日志中可以看出,通过反射获取到的Method在Dalvik中对应的对象不是Method,所以强制转换成Method导致打出来的数据不对。
但是Class数据是没问题的,原因是Java中的对象在Dalvik中都是继承自Object的,而Object的第一个成员就是ClassObject,所以
任意两个Object相互转换,就算其余的成员都不一样,但是ClassObject总是对的上的。这个ClassObject就是描述该Object对应的类。
通过上面这个实验,有这么几个问题:
1. jmethodID和Method有什么关系?
2. 这个反射到的Method和Dalvik中的Method有什么联系?
3. slot是个什么东西?
先看第一个问题,在代码里找了一圈jmethodID,只找到如下两行
这个_jmethodID是个黑箱类型,里面怎么定义的外面是不知道的。那它和Method有什么关系呢?在dalvik/vm/jni.c中找到这么个函数
可见,这里面可以直接将methodID转换成Method *,虽然这并不能说明两者就是完全相同的,但是我们可以认为_jmethodID的第一个成员就是Method。
再来看第二个问题,这个反射到的Method和Dalvik中的Method有什么联系?同样在dalvik/vm/jni.c中找到这么一个函数
这个函数的作用就是将反射的Method对象转换成真正的Method对象。我们来看看它是怎么做到的
原来它是通过slot来获取到的!!
那接下来的问题就很清楚了,搞定了slot一切都好说。看看dvmSlotToMethod的实现:
看起来,如果是virtualMethods,则slot >0,如果是正常函数,则slot < 0。
再来看看slot是在哪里设置的,仍然在dalvik/vm/reflect.c中:
可见,这个slot就是Method在ClassObject的函数指针数组中的索引。
这样,三者都通了,一个是slot,一个是Java层反射的Method对象,一个是Dalvik中的Method。
总结一下,我们要Hook一个Java层的函数,则需要获取其在Dalvik中对应的Method。获取方式有两种:
传入函数名和函数签名,然后在Native层调用getMethodID获取到jmethodID,但是函数签名得我们自己拼很麻烦。
传入函数所在的Class和对应的slot,然后在Native层调用dvmSlotToMethod即可,slot可以通过在Java层反射来获取
因为要Hook Java层的某个函数,首先得获取该函数在Dalvik中对应的Method数据结构,然后修改其函数指针指向另外一个函数,从而达到Hook的效果。
首先提出一个猜想,Java层通过反射可以获取函数的Method,而这个Method是否和Dalvik中的Method是一回事呢?
新建一个工程来验证一下(工程下载地址)
分别通过三种方式来获取Method:通过反射、通过slot、通过函数签名。
首先新建一个类Test.class,如下
public class Test { static { System.loadLibrary("test"); } public static void hello() { Log.i("bush", "hello called"); } public native static void showMethodFromReflect(Method method); public native static void showMethodFromSlot(Class<?> clazz, int slot); public native static void showMethodFromSig(Class<?> clazz, String methodName, String methodSig); }
Native层的实现如下:
#include <jni.h> #include <stdlib.h> #define ANDROID_SMP 0 #include "Dalvik.h" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { JNIEnv *env = NULL; jint result = -1; if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) { return result; } return JNI_VERSION_1_6; } void showMethodInfo(Method *method) { ALOGI("method: %p", method); ClassObject *classObject = method->clazz; ALOGD("clazz->descriptor: %s", classObject->descriptor); ALOGD("clazz->sourceFile: %s", classObject->sourceFile); ALOGD("name: %s", method->name); ALOGD("shorty: %s", method->shorty); ALOGD("accessFlags: %d", method->accessFlags); } extern "C" JNIEXPORT JNICALL void Java_com_example_dingjikerbo_myapplication_Test_showMethodFromReflect(JNIEnv *env, jclass object, jobject methodObj) { Method* methodObject = (Method *) dvmDecodeIndirectRef(dvmThreadSelf(), methodObj); showMethodInfo(methodObject); } extern "C" JNIEXPORT JNICALL void Java_com_example_dingjikerbo_myapplication_Test_showMethodFromSlot(JNIEnv *env, jclass clazz, jclass clazzToHook, jint slot) { ClassObject *clazzObject = (ClassObject *) dvmDecodeIndirectRef(dvmThreadSelf(), clazzToHook); Method *method = dvmSlotToMethod(clazzObject, slot); showMethodInfo(method); } extern "C" JNIEXPORT JNICALL void Java_com_example_dingjikerbo_myapplication_Test_showMethodFromSig(JNIEnv *env, jclass object, jclass clazz, jstring methodName, jstring methodSig) { const char *_methodName = env->GetStringUTFChars(methodName, NULL); const char *_methodSig = env->GetStringUTFChars(methodSig, NULL); jmethodID methodId = env->GetMethodID(clazz, _methodName, _methodSig); if (methodId == NULL) { env->ExceptionClear(); methodId = env->GetStaticMethodID(clazz, _methodName, _methodSig); } if (methodId != NULL) { Method *method = (Method *) methodId; showMethodInfo(method); } env->ReleaseStringUTFChars(methodName, _methodName); env->ReleaseStringUTFChars(methodSig, _methodSig); }
MainActivity中调用如下:
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); showMethodFromReflect(); showMethodFromSlot(); showMethodFromSig(); } private void showMethodFromReflect() { Method method = MethodUtils.getAccessibleMethod(Test.class, "hello"); Test.showMethodFromReflect(method); } private void showMethodFromSlot() { Method method = MethodUtils.getAccessibleMethod(Test.class, "hello"); Field field = FieldUtils.getField(Method.class, "slot", true); try { int slot = field.getInt(method); Test.showMethodFromSlot(Test.class, slot); } catch (IllegalAccessException e) { e.printStackTrace(); } } private void showMethodFromSig() { Test.showMethodFromSig(Test.class, "hello", "()V"); } }
运行后日志如下:
method: 0x42d033d0 clazz->descriptor: Ljava/lang/reflect/Method; clazz->sourceFile: Method.java name: (null) shorty: (null) accessFlags: 0 method: 0x6d910c00 clazz->descriptor: Lcom/example/dingjikerbo/myapplication/Test; clazz->sourceFile: Test.java name: hello shorty: V accessFlags: 9 method: 0x6d910c00 clazz->descriptor: Lcom/example/dingjikerbo/myapplication/Test; clazz->sourceFile: Test.java name: hello shorty: V accessFlags: 9
从日志中可以看出,通过反射获取到的Method在Dalvik中对应的对象不是Method,所以强制转换成Method导致打出来的数据不对。
但是Class数据是没问题的,原因是Java中的对象在Dalvik中都是继承自Object的,而Object的第一个成员就是ClassObject,所以
任意两个Object相互转换,就算其余的成员都不一样,但是ClassObject总是对的上的。这个ClassObject就是描述该Object对应的类。
通过上面这个实验,有这么几个问题:
1. jmethodID和Method有什么关系?
2. 这个反射到的Method和Dalvik中的Method有什么联系?
3. slot是个什么东西?
先看第一个问题,在代码里找了一圈jmethodID,只找到如下两行
struct _jmethodID; /* opaque structure */ typedef struct _jmethodID* jmethodID; /* method IDs */
这个_jmethodID是个黑箱类型,里面怎么定义的外面是不知道的。那它和Method有什么关系呢?在dalvik/vm/jni.c中找到这么个函数
static jobject ToReflectedMethod(JNIEnv* env, jclass jcls, jmethodID methodID, jboolean isStatic) { JNI_ENTER(); ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls); Object* obj = dvmCreateReflectObjForMethod(clazz, (Method*) methodID); dvmReleaseTrackedAlloc(obj, NULL); jobject jobj = addLocalReference(env, obj); JNI_EXIT(); return jobj; }
可见,这里面可以直接将methodID转换成Method *,虽然这并不能说明两者就是完全相同的,但是我们可以认为_jmethodID的第一个成员就是Method。
再来看第二个问题,这个反射到的Method和Dalvik中的Method有什么联系?同样在dalvik/vm/jni.c中找到这么一个函数
/* * Given a java.lang.reflect.Method or .Constructor, return a methodID. */ static jmethodID FromReflectedMethod(JNIEnv* env, jobject jmethod) { JNI_ENTER(); jmethodID methodID; Object* method = dvmDecodeIndirectRef(env, jmethod); methodID = (jmethodID) dvmGetMethodFromReflectObj(method); JNI_EXIT(); return methodID; }
这个函数的作用就是将反射的Method对象转换成真正的Method对象。我们来看看它是怎么做到的
Method* dvmGetMethodFromReflectObj(Object* obj) { ClassObject *clazz = (ClassObject*)dvmGetFieldObject(obj, gDvm.offJavaLangReflectMethod_declClass); int slot = dvmGetFieldInt(obj, gDvm.offJavaLangReflectMethod_slot); return dvmSlotToMethod(clazz, slot); }
原来它是通过slot来获取到的!!
那接下来的问题就很清楚了,搞定了slot一切都好说。看看dvmSlotToMethod的实现:
Method* dvmSlotToMethod(ClassObject* clazz, int slot) { if (slot < 0) { slot = -(slot+1); assert(slot < clazz->directMethodCount); return &clazz->directMethods[slot]; } else { assert(slot < clazz->virtualMethodCount); return &clazz->virtualMethods[slot]; } }
看起来,如果是virtualMethods,则slot >0,如果是正常函数,则slot < 0。
再来看看slot是在哪里设置的,仍然在dalvik/vm/reflect.c中:
static int methodToSlot(const Method* meth) { ClassObject* clazz = meth->clazz; int slot; if (dvmIsDirectMethod(meth)) { slot = meth - clazz->directMethods; slot = -(slot+1); } else { slot = meth - clazz->virtualMethods; } return slot; }
可见,这个slot就是Method在ClassObject的函数指针数组中的索引。
这样,三者都通了,一个是slot,一个是Java层反射的Method对象,一个是Dalvik中的Method。
总结一下,我们要Hook一个Java层的函数,则需要获取其在Dalvik中对应的Method。获取方式有两种:
传入函数名和函数签名,然后在Native层调用getMethodID获取到jmethodID,但是函数签名得我们自己拼很麻烦。
传入函数所在的Class和对应的slot,然后在Native层调用dvmSlotToMethod即可,slot可以通过在Java层反射来获取
相关文章推荐
- springmvc系列之一(原理)
- java设计模式
- Java反射机制详解:从classLoader到反射机制再到抽象工厂设计模式
- 从iOS学习Java(1)
- Spring学习笔记 1. 尚硅谷_佟刚_Spring_HelloWorld
- Java学习笔记·Servlet parameter参数传递utf-8文字编码正常显示
- 第K大数 算法分析、设计与实现(Java)
- 2014-5-22 java.lang.OutOfMemoryError: Java heap space的一次诊断
- Java 接口归纳
- java中泛型的继承
- Eclipse连接mysql
- java集合:链表:Java:LinkedList<E>
- spring mvc
- Java final 关键字
- java编译运行详解
- java 内部类 和 匿名内部类
- java成员方法
- Win7 64位安装JDK的步骤,并且附上测试程序
- java初始化之变量初始化
- Java super和this的异同总结