zoukankan      html  css  js  c++  java
  • Android Hook Dexposed原理小析

    dexposed是阿里巴巴在xposed框架上面开发的hotpatch一套框架

    当然hotpatch的方式有很多,这里先介绍下dexposed原理

    Demo中有个test函数, 在调用hook之前正常返回”11111”; 调用hook之后, 却返回”newTestMethod”, 被我们给修改

    public class Demo
    {
        String TAG = "===[hookdemo]===";
    
        public static String staticTest(String param1)
        {
            return "staticTest";
        }
    
        public String test(String param1)
        {
            return "11111";
        }
    
        public void demo()
        {
            String param1 = "param1";
            Log.d(TAG, "===========before hook test:" + this.test(param1));
            hook(Demo1.class, "test", "(Ljava/lang/String;)Ljava/lang/String;");
            Log.d(TAG, "===========after hook test:" + this.test(param1));
    
            Log.d(TAG, "===========before hook staticTest:" + this.staticTest(param1));
            hook(Demo1.class, "staticTest", "(Ljava/lang/String;)Ljava/lang/String;");
            Log.d(TAG, "===========after hook staticTest:" + this.staticTest(param1));
        }
    
        private native void hook(Class<?> clazzToHook, String methodName, String methodSig);
    }

    ndk 中的部分

    #include <jni.h>
    #include "log.h"
    #include "Dalvik.h"
    
    static void showMethodInfo(const Method* method)
    {
        //看看method的各个属性都是啥:
        LOGD("accessFlags:%d",method->accessFlags);
        LOGD("clazz->descriptor:%s",method->clazz->descriptor);
        LOGD("clazz->sourceFile:%s",method->clazz->sourceFile);
        LOGD("methodIndex:%d",method->methodIndex);
        LOGD("name:%s",method->name);
        LOGD("shorty:%s",method->shorty);
    }
    
    /**
     * 使用jni GetMethodID 方法获取jmethodID 强制转为 Method 的hook 方法 示例
     */
    static void newTestMethod(const u4* args, JValue* pResult,
                              const Method* method, struct Thread* self) {
    
        // args 是原来函数的参数数组, 原来test函数只有一个String型参数
        // 并且要注意, 如果是不是static函数, 下标0 是函数所在类的实例obj
        // 在dvm中Object,  jni 中的jobject 和 java 中的 Object类 都不是同一个东西
        // String类对应StringObject
        // 取出参数打印出来看看
        StringObject* param1 = NULL;
    
        if(dvmIsStaticMethod(method))
            param1 = (StringObject*)args[0];
        else
            param1 = (StringObject*)args[1];
        LOGD("param1:%s",dvmCreateCstrFromString(param1));
    
        //JValue 是个union ,要返回int 就 pResult->i=1; 返回Object对象就 pResult->l = ojb;
        // 但是, 在dvm中的Object,  jni 中的jobject 和 java 中的 Object类 都不是同一个东西
        // 所以, 我们这里使用dvm的函数来创建一个StringObject*
        pResult->l = dvmCreateStringFromCstr("newTestMethod");
    
        // 一般情况应该使用宏 : RETURN_XXX(result);
        return;
    }
    
    extern "C" JNIEXPORT void JNICALL
    Java_com_zhaoxiaodan_hookdemo_Demo1_hook(JNIEnv *env, jobject instance, jobject clazzToHook,
                                                    jstring methodName_, jstring methodSig_) {
    
        const char *methodName = env->GetStringUTFChars(methodName_, 0);
        const char *methodSig = env->GetStringUTFChars(methodSig_, 0);
    
        jmethodID methodIDToHook = env->GetMethodID((jclass) clazzToHook,methodName,methodSig);
    
        // 找不到有可能是个static
        if(nullptr == methodIDToHook){
            env->ExceptionClear();
            methodIDToHook = env->GetStaticMethodID((jclass) clazzToHook,methodName,methodSig);
        }
    
    
        if(methodIDToHook != nullptr)
        {
            //主要在这里替换
            //jmethodID 在dvm里实际上就是个Method 结构体
            Method* method = (Method*) methodIDToHook;
    
            //看看method的各个属性都是啥:
            showMethodInfo(method);
    
            //设置Method 的 accessFlags 为 枚举型
            // ACC_NATIVE 表示 这个method 切换成了一个native 方法
            // 这个枚举 在 dalvik/libdex/DexFile.h
            // 类似:
            // ACC_PUBLIC       = 0x00000001,       // class, field, method, ic
            // ACC_PRIVATE      = 0x00000002,       // field, method, ic
            // ACC_PROTECTED    = 0x00000004,       // field, method, ic
            SET_METHOD_FLAG(method, ACC_NATIVE);
    
            //既然是一个native方法, 那就把 nativeFunc 指针指向我们的hook, 用来替换test的新方法
            method->nativeFunc = &newTestMethod;
    
            // registersSize是函数栈大小, insSize是参数占用大小
            // 如果是native方法, 就没有额外开销了
            // 所有开销就是参数占用, 所以把它设置成跟参数占用空间
            method->registersSize=method->insSize;
    
            //未知
            method->outsSize=0;
        }
    
        env->ReleaseStringUTFChars(methodName_, methodName);
        env->ReleaseStringUTFChars(methodSig_, methodSig);
    }

    原理就是, Method结构体表示了一个java层函数, 而其中的accessFlags属性如果是ACC_NATIVE ,

    dvm在call 原java层函数的时候, 则会转向调用 属性nativeFunc 所指向的函数

    所以我们把不是native的java层函数的accessFlags强制改为ACC_NATIVE, 然后把nativeFunc 指向我们的新函数,

    则完成了方法的修改只不过, 这里使用native方法替换了java层的原方法

     
  • 相关阅读:
    TC Asia Competition
    Codeforces 258 Div2
    斯坦纳树
    <算法竞赛入门经典> 第8章 贪心+递归+分治总结
    UVALive 6602 Counting Lattice Squares
    UVALive 6609 Minimal Subarray Length (查找+构建排序数组)
    vue中路由以及动态路由跳转
    sublime安装
    js数组转对象
    如何将变量做为一个对象的key,push进一个数组?
  • 原文地址:https://www.cnblogs.com/lzl-sml/p/5203197.html
Copyright © 2011-2022 走看看