Hook Java的的一个改进版本

原创 2014年09月29日 11:17:29

Hook Java的的一个改进版本

《注入安卓进程,并Hook java世界的方法》这篇好文相信大家都看这,里面所提到的方法估计大家也都试过。不过里面的所用的方法,我发现有两个可以改进的地方。

改进点一:更简单地修改java方法为本地方法

...
    // hook method
    int argsSize = calcMethodArgsSize(method->shorty);
    if (!dvmIsStaticMethod(method))
        argsSize++;


    SET_METHOD_FLAG(method, ACC_NATIVE);
    method->registersSize = method->insSize = argsSize;
    method->outsSize = 0;
    method->jniArgInfo = dvmComputeJniArgInfo(method->shorty);


    // save info to insns
    method->insns = (u2*)info;


    // bind the bridge func,only one line
    method->nativeFunc = method_handler;
    LOGI("[+] %s->%s was hooked\n", classDesc, methodName);
    ...


直接把method->nativeFunc即可,无需重新调用JNIEnv的RegisterNatives方法,其中method_handler可以是下面两种形式之一:

typedef void (*DalvikBridgeFunc)(const u4* args, JValue* pResult, const Method* method, struct Thread* self);
typedef void (*DalvikNativeFunc)(const u4* args, JValue* pResult);

这样有一个好处,就是所有java方法都可以统一指向同一个native func,而不需要像为每一个java method方法指定一个native func。

改进点二:方法回调避免线程安全问题

原来的方法,是这样的

//hook之前先拷贝
  uint mlen = sizeof(Method);
  Method *oldMeth = (Method*)malloc(mlen);
  memcpy(oldMeth,method,mlen);
  info->odlMethod = oldMeth;
  info->curMethod = method;


  //回调后再拷贝回来,再通过jni->callXXXXMethod调用,之后再重新hook
  memcpy(hi->curMethod,hi->odlMethod,mlen);
  jmethodID om = (jmethodID)hi->curMethod;
  jenv->CallVoidMethod(me,om,gDevice_Sensors);
  ClassMethodHook(jenv,&baiduhookInfos[0]);


这个方法,其实是有线程安全问题的,其中在dalvik中,有很多方法可以直接调用Method对象,比如dvmCallMethod, dvmCallMethodA, dvmCallMethodV,dvmInvokeMethod等等。针对DalvikBridgeFunc和DalvikNativeFunc的参数,我最后选择使用dvmInvokeMethod,这个函数的原型是这样的:

Object* dvmInvokeMethod(Object* obj, const Method* method, ArrayObject* argList, ArrayObject* params, ClassObject* returnType, bool noAccessCheck)

其中,obj是this或者null(如果是static方法),method可以直接使用hook之前copy的对象,比较麻烦是argList,params和returnType的获取。获取argList的方法,我在Proxy.cpp中到了现成的boxMethodArgs方法,而returnType通过Reflect.h中dvmGetBoxedReturnType的方法也可以获取,而剩下的params只能自己写代码了,下面是我的代码:

STATIC ArrayObject* dvmGetMethodParamTypes(const Method* method, const char* methodsig){
    /* count args */
    size_t argCount = dexProtoGetParameterCount(&method->prototype);
    STATIC ClassObject* java_lang_object_array = dvmFindSystemClass("[Ljava/lang/Object;");


    /* allocate storage */
    ArrayObject* argTypes = dvmAllocArrayByClass(java_lang_object_array, argCount, ALLOC_DEFAULT);
    if(argTypes == NULL){
        return NULL;
    }


    Object** argObjects = (Object**) argTypes->contents;
    const char *desc = (const char *)(strchr(methodsig, '(') + 1);


    /*
     * Fill in the array.
     */
    size_t desc_index = 0;
    size_t arg_index = 0;
    bool isArray = false;
    char descChar = desc[desc_index];


    while (descChar != ')') {


        switch (descChar) {
        case 'Z':
        case 'C':
        case 'F':
        case 'B':
        case 'S':
        case 'I':
        case 'D':
        case 'J':
            if(!isArray){
                argObjects[arg_index++] = dvmFindPrimitiveClass(descChar);
                isArray = false;
            }else{
                char buf[3] = {0};
                memcpy(buf, desc + desc_index - 1, 2);
                argObjects[arg_index++] = dvmFindSystemClass(buf);
            }


            desc_index++;
            break;


        case '[':
            isArray = true;
            desc_index++;
            break;


        case 'L':
            int s_pos = desc_index, e_pos = desc_index;
            while(desc[++e_pos] != ';');
            s_pos = isArray ? s_pos - 1 : s_pos;
            isArray = false;


            size_t len = e_pos - s_pos + 1;
            char buf[128] = { 0 };
            memcpy((void *)buf, (const void *)(desc + s_pos), len);
            argObjects[arg_index++] = dvmFindClass(buf);
            desc_index = e_pos + 1;
            break;
        }


        descChar = desc[desc_index];
    }


    return argTypes;
}


通过上面几个类型的获取之后,最后再看一下整个method hook的实现,过程其实大同小异,不过直接把上述提及的向种类型信息预先获取并保存到method->insns里头了:

extern int __attribute__ ((visibility ("hidden"))) dalvik_java_method_hook(JNIEnv* env, HookInfo *info) {
    const char* classDesc = info->classDesc;
    const char* methodName = info->methodName;
    const char* methodSig = info->methodSig;
    const bool isStaticMethod = info->isStaticMethod;


    jclass classObj = dvmFindJNIClass(env, classDesc);
    if (classObj == NULL) {
        LOGE("[-] %s class not found", classDesc);
        return -1;
    }


    jmethodID methodId =
            isStaticMethod ?
                    env->GetStaticMethodID(classObj, methodName, methodSig) :
                    env->GetMethodID(classObj, methodName, methodSig);


    if (methodId == NULL) {
        LOGE("[-] %s->%s method not found", classDesc, methodName);
        return -1;
    }




    // backup method
    Method* method = (Method*) methodId;
    if(method->nativeFunc == method_handler){
        LOGW("[*] %s->%s method had been hooked", classDesc, methodName);
        return -1;
    }
    Method* bakMethod = (Method*) malloc(sizeof(Method));
    memcpy(bakMethod, method, sizeof(Method));


    // init info
    info->originalMethod = (void *)bakMethod;
    info->returnType = (void *)dvmGetBoxedReturnType(bakMethod);
    info->paramTypes = dvmGetMethodParamTypes(bakMethod, info->methodSig);


    // hook method
    int argsSize = calcMethodArgsSize(method->shorty);
    if (!dvmIsStaticMethod(method))
        argsSize++;


    SET_METHOD_FLAG(method, ACC_NATIVE);
    method->registersSize = method->insSize = argsSize;
    method->outsSize = 0;
    method->jniArgInfo = dvmComputeJniArgInfo(method->shorty);


    // save info to insns
    method->insns = (u2*)info;


    // bind the bridge func,only one line
    method->nativeFunc = method_handler;
    LOGI("[+] %s->%s was hooked\n", classDesc, methodName);


    return 0;
}

然后是method_handler的实现,这个方法是所有java方法的跳转函数,所以在这里可以注册callback,不过这部分逻辑我没有做上,有兴趣的朋友可以加上。

STATIC void method_handler(const u4* args, JValue* pResult, const Method* method, struct Thread* self){
    HookInfo* info = (HookInfo*)method->insns; //get hookinfo pointer from method-insns
    LOGI("entry %s->%s", info->classDesc, info->methodName);


    Method* originalMethod = reinterpret_cast<Method*>(info->originalMethod);
    Object* thisObject = (Object*)args[0];


    ArrayObject* argTypes = dvmBoxMethodArgs(originalMethod, args + 1); 
    pResult->l = (void *)dvmInvokeMethod(thisObject, originalMethod, argTypes, (ArrayObject *)info->paramTypes, (ClassObject *)info->returnType, true);


    dvmReleaseTrackedAlloc((Object *)argTypes, self);
}


最后通过dvmInvokeMethod就可以直接调回原来的函数了。

最后

写这个代码,主要是因为我在工作中要注入到某个系统进程,然后要hook java中的某些方法,但用cydia和xposed感觉太笨重了,特别是xposed,里面的很多参数的boxed/unboxed都是通过jni模块自动转换的,整个框架已经离不开dex文件了。

所以才想自己实现一套纯本地的java hook代码,而《注入安卓进程,并Hook java世界的方法》所介绍的方法,我感觉用起来不太方便,跟cydia和xposed两个框架的主要区别就是缺少了一个“中转函数”,所以而有了本码。

代码我上传到github,目前只有java hook,我打算把目前的hook技术都集成到这里,包括inline hook, elf hook等等。

Java HOOK - 钩子机制扫盲

一、什么是HOOK(钩子)          对于Windows系统,它是建立在事件驱动机制上的,说白了就是整个系统都是通过消息传递实现的。hook(钩子)是一种特殊的消息处理机制,它可以监视系统或者...
  • hjxwudiha931721
  • hjxwudiha931721
  • 2017年07月25日 14:14
  • 969

注入安卓进程,并hook java世界的方法

标 题: 【原创】注入安卓进程,并hook java世界的方法 作 者: malokch 时 间: 2014-03-28,14:16:46 链 接: http://bbs.pediy.com...
  • dj0379
  • dj0379
  • 2014年09月19日 00:41
  • 3254

跟着鬼哥学android java hook(二)

第一篇文章读完之后,大家在熟悉了cydia的hook框架了,然后我们就开始下一步的学习。 本篇文章,主要是修改正常软件中方法的返回值,大家可以根据这个思路去延伸扩展,不要局限于此文一个思路。 0x...
  • guiguzi1110
  • guiguzi1110
  • 2014年08月29日 13:18
  • 3375

Android Hook Java

实现了一个脱离XPosed框架的Android Hook Java方法。其原理是注入某系统进程,使该进程加载某动态链接库,执行该库里的某函数,通过该函数执行某jar包里的java函数,然后执行hook...
  • phoebe_2012
  • phoebe_2012
  • 2015年07月23日 16:00
  • 1176

Java 实现 Hook 对鼠标键盘监听

Java 实现全局(全操作系统)的鼠标和键盘监控,使用的是SWT Win32 Extension 项目,在以前若使用Java恐怕是很难实现的!SWT Win32 Extension 让Java拥有了许...
  • linzhanggeorge
  • linzhanggeorge
  • 2009年08月06日 20:09
  • 5250

JAVA虚拟机关闭钩子(Shutdown Hook)

Java程序经常也会遇到进程挂掉的情况,一些状态没有正确的保存下来,这时候就需要在JVM关掉的时候执行一些清理现场的代码。JAVA中的ShutdownHook提供了比较好的方案。 JDK提供了Java...
  • u013256816
  • u013256816
  • 2015年12月24日 15:48
  • 12783

模板方法设计模式扩展(Hook Method)

package Template_Method_Pattern;import java.util.Scanner;/** * 模板方法模式的扩展 * 钩子方法(Hook Method) * @a...
  • kongshuchen
  • kongshuchen
  • 2016年05月11日 22:16
  • 680

Hook Java的的一个改进版本

《注入安卓进程,并Hook java世界的方法》这篇好文相信大家都看这,里面所提到的方法估计大家也都试过。不过里面的所用的方法,我发现有两个可以改进的地方。 改进点一:更简单地修改java方法为本地...
  • u010164190
  • u010164190
  • 2016年07月17日 15:36
  • 455

Android安全:Hook技术

一、Hook技术 1.Hook英文翻译为“钩子”,而钩子就是在事件传送到终点前截获并监控事件的传输,像个钩子钩上事件一样,并且能够在钩上事件时,处理一些自己特定的事件; 2.Hook使它能够将自己...
  • p106786860
  • p106786860
  • 2016年08月15日 18:39
  • 15275

Android 动态代理以及利用动态代理实现 ServiceHook

Android 利用 ServiceHook 实现特殊功能
  • zhao_zepeng
  • zhao_zepeng
  • 2017年02月25日 20:44
  • 7547
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Hook Java的的一个改进版本
举报原因:
原因补充:

(最多只允许输入30个字)