3

[原创]注入安卓进程,并hook java世界的方法

malokch 2014-3-28 14:16 166381
说明:
安卓系统的可执行对象有两个世界,一个是Linux Native世界,一个是Java世界.两个世界能够通过jvm产生交互,具体来说就是通过jni技术进行互相干涉.但是在正常情况下,只能在Java世界通过jni调用native方法,二native不能在没有任何java上的支持下干涉java世界.
在一些应用中,我们需要对一个app的java世界进行干涉.再说到linux上的进程注入技术,已不用我多讲,但是传统的linux进程注入技术在安卓上只能进入目标进程的native世界.
于是本教程是要注入别的进程,并hook java世界的java 方法!
文章长,详情见附件

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

说明:
安卓系统的可执行对象有两个世界,一个是Linux Native世界,一个是Java世界.两个世界能够通过jvm产生交互,具体来说就是通过jni技术进行互相干涉.但是在正常情况下,只能在Java世界通过jni调用native方法,二native不能在没有任何java上的支持下干涉java世界.

在一些应用中,我们需要对一个app的java世界进行干涉.再说到linux上的进程注入技术,已不用我多讲,但是传统的linux进程注入技术在安卓上只能进入目标进程的native世界.

于是本教程是要注入别的进程,并hook java世界的java 方法!

条件:

1)        手机已root
2)        布置好了的ndk环境
3)        网友贡献的inject代码

由于安卓上的进程注入网上已经有很多方案了,这里就不列举了,这里就假设读者已经能够将so注入到别的进程并顺利运行了.

首先贴一下这次的目标
package com.example.testar;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

import dalvik.system.DexClassLoader;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.text.GetChars;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.Button;

public class MainActivity extends Activity {
    private final Map<String, ClassLoader> mLoaders = new HashMap<String, ClassLoader>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button btn = (Button) findViewById(R.id.button1);
        btn.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                WifiManager wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
                WifiInfo info = wifi.getConnectionInfo();
                System.out.println("Wifi mac :" + info.getMacAddress());
                System.out.println("return " + test());
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    private String test() {
        return "real";
    }
}

我们的目标是上面的test()方法,我们要改变其返回值.
接下来看看我们要注入到目标进程的so.cpp, MethodHooker.cpp
so.cpp:
#include "jni.h"
#include "android_runtime/AndroidRuntime.h"
#include "android/log.h"
#include "stdio.h"
#include "stdlib.h"
#include "MethodHooker.h"
#include <utils/CallStack.h>
#include "art.h"
#define log(a,b) __android_log_write(ANDROID_LOG_INFO,a,b); // LOG Ѝ:info
#define log_(b) __android_log_write(ANDROID_LOG_INFO,"JNI_LOG_INFO",b); // LOG Ѝ:info
extern "C" void InjectInterface(char*arg){
    log_("*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*");
    log_("*-*-*-*-*-* Injected so *-*-*-*-*-*-*-*");
    log_("*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*");
    Hook();
    log_("*-*-*-*-*-*-*- End -*-*-*-*-*-*-*-*-*-*");
}

extern "C" JNIEXPORT jstring JNICALL Java_com_example_testar_InjectApplication_test(JNIEnv *env, jclass clazz)
{
    Abort_();
    return env->NewStringUTF("haha ");;
}


[B]MethodHooker.cpp:[/B]
#include "MethodHooker.h"
#include "jni.h"
#include "android_runtime/AndroidRuntime.h"
#include "android/log.h"
#include "stdio.h"
#include "stdlib.h"
#include "native.h"
#include <dlfcn.h>
#define ANDROID_SMP 0
#include "Dalvik.h"
#include "alloc/Alloc.h"

#include "art.h"

#define ALOG(...) __android_log_print(ANDROID_LOG_VERBOSE, __VA_ARGS__)

static bool g_bAttatedT;
static JavaVM *g_JavaVM;

void init()
{
    g_bAttatedT = false;
    g_JavaVM = android::AndroidRuntime::getJavaVM();
}

static JNIEnv *GetEnv()
{
 int status;
    JNIEnv *envnow = NULL;
    status = g_JavaVM->GetEnv((void **)&envnow, JNI_VERSION_1_4);
    if(status < 0)
    {
        status = g_JavaVM->AttachCurrentThread(&envnow, NULL);
        if(status < 0)
        {
            return NULL;
        }
        g_bAttatedT = true;
    }
    return envnow;
}

static void DetachCurrent()
{
    if(g_bAttatedT)
    {
        g_JavaVM->DetachCurrentThread();
    }
}

static int computeJniArgInfo(const DexProto* proto)
{
    const char* sig = dexProtoGetShorty(proto);
    int returnType, jniArgInfo;
    u4 hints;

    /* The first shorty character is the return type. */
    switch (*(sig++)) {
    case 'V':
        returnType = DALVIK_JNI_RETURN_VOID;
        break;
    case 'F':
        returnType = DALVIK_JNI_RETURN_FLOAT;
        break;
    case 'D':
        returnType = DALVIK_JNI_RETURN_DOUBLE;
        break;
    case 'J':
        returnType = DALVIK_JNI_RETURN_S8;
        break;
    case 'Z':
    case 'B':
        returnType = DALVIK_JNI_RETURN_S1;
        break;
    case 'C':
        returnType = DALVIK_JNI_RETURN_U2;
        break;
    case 'S':
        returnType = DALVIK_JNI_RETURN_S2;
        break;
    default:
        returnType = DALVIK_JNI_RETURN_S4;
        break;
    }

    jniArgInfo = returnType << DALVIK_JNI_RETURN_SHIFT;

    hints = dvmPlatformInvokeHints(proto);

    if (hints & DALVIK_JNI_NO_ARG_INFO) {
        jniArgInfo |= DALVIK_JNI_NO_ARG_INFO;
    } else {
        assert((hints & DALVIK_JNI_RETURN_MASK) == 0);
        jniArgInfo |= hints;
    }

    return jniArgInfo;
}

int ClearException(JNIEnv *jenv){
    jthrowable exception = jenv->ExceptionOccurred();
    if (exception != NULL) {
        jenv->ExceptionDescribe();
        jenv->ExceptionClear();
        return true;
    }
    return false;
}

bool isArt(){
    return true;
}

static jclass findAppClass(JNIEnv *jenv,const char *apn){
    //������oaders
    jclass clazzApplicationLoaders = jenv->FindClass("android/app/ApplicationLoaders");
    jthrowable exception = jenv->ExceptionOccurred();
    if (ClearException(jenv)) {
        ALOG("Exception","No class : %s", "android/app/ApplicationLoaders");
        return NULL;
    }
    jfieldID fieldApplicationLoaders = jenv->GetStaticFieldID(clazzApplicationLoaders,"gApplicationLoaders","Landroid/app/ApplicationLoaders;");
    if (ClearException(jenv)) {
        ALOG("Exception","No Static Field :%s","gApplicationLoaders");
        return NULL;
    }
    jobject objApplicationLoaders = jenv->GetStaticObjectField(clazzApplicationLoaders,fieldApplicationLoaders);
    if (ClearException(jenv)) {
        ALOG("Exception","GetStaticObjectField is failed [%s","gApplicationLoaders");
        return NULL;
    }
    jfieldID fieldLoaders = jenv->GetFieldID(clazzApplicationLoaders,"mLoaders","Ljava/util/Map;");
    if (ClearException(jenv)) {
        ALOG("Exception","No Field :%s","mLoaders");
        return NULL;
    }
    jobject objLoaders = jenv->GetObjectField(objApplicationLoaders,fieldLoaders);
    if (ClearException(jenv)) {
        ALOG("Exception","No object :%s","mLoaders");
        return NULL;
    }
    //̡ȡmap֐Ķalues
    jclass clazzHashMap = jenv->GetObjectClass(objLoaders);
    jmethodID methodValues = jenv->GetMethodID(clazzHashMap,"values","()Ljava/util/Collection;");
    jobject values = jenv->CallObjectMethod(objLoaders,methodValues);

    jclass clazzValues = jenv->GetObjectClass(values);
    jmethodID methodToArray = jenv->GetMethodID(clazzValues,"toArray","()[Ljava/lang/Object;");
    if (ClearException(jenv)) {
        ALOG("Exception","No Method:%s","toArray");
        return NULL;
    }

    jobjectArray classLoaders = (jobjectArray)jenv->CallObjectMethod(values,methodToArray);
    if (ClearException(jenv)) {
        ALOG("Exception","CallObjectMethod failed :%s","toArray");
        return NULL;
    }

        int size = jenv->GetArrayLength(classLoaders);

        for(int i = 0 ; i < size ; i ++){
            jobject classLoader = jenv->GetObjectArrayElement(classLoaders,i);
            jclass clazzCL = jenv->GetObjectClass(classLoader);
            jmethodID loadClass = jenv->GetMethodID(clazzCL,"loadClass","(Ljava/lang/String;)Ljava/lang/Class;");
            jstring param = jenv->NewStringUTF(apn);
            jclass tClazz = (jclass)jenv->CallObjectMethod(classLoader,loadClass,param);
            if (ClearException(jenv)) {
                ALOG("Exception","No");
                continue;
            }
            return tClazz;
        }
    ALOG("Exception","No");
    return NULL;
}



bool HookDalvikMethod(jmethodID jmethod){
    Method *method = (Method*)jmethod;
    //ؼ!!Ŀ귽ОĎnative
    SET_METHOD_FLAG(method, ACC_NATIVE);

    int argsSize = dvmComputeMethodArgsSize(method);
    if (!dvmIsStaticMethod(method))
        argsSize++;

    method->registersSize = method->insSize = argsSize;

    if (dvmIsNativeMethod(method)) {
        method->nativeFunc = dvmResolveNativeMethod;
        method->jniArgInfo = computeJniArgInfo(&method->prototype);
    }
}

bool ClassMethodHook(HookInfo info){

    JNIEnv *jenv = GetEnv();

    jclass clazzTarget = jenv->FindClass(info.tClazz);
    if (ClearException(jenv)) {
        ALOG("Exception","ClassMethodHook[Can't find class:%s in bootclassloader",info.tClazz);

        clazzTarget = findAppClass(jenv,info.tClazz);
        if(clazzTarget == NULL){
            ALOG("Exception","%s","Error in findAppClass");
            return false;
        }
    }

    jmethodID method = jenv->GetMethodID(clazzTarget,info.tMethod,info.tMeihodSig);
    if(method==NULL){
        ALOG("Exception","ClassMethodHook[Can't find method:%s",info.tMethod);
        return false;
    }

    if(isArt()){
        HookArtMethod(jenv,method);
    }else{
        HookDalvikMethod(method);
    }

    JNINativeMethod gMethod[] = {
        {info.tMethod, info.tMeihodSig, info.handleFunc},
    };

    //funcΪNULLʱהА������������չɍ
    if(info.handleFunc != NULL){
        //ؼ!!Ŀ귽؁הҥĮative
        if (jenv->RegisterNatives(clazzTarget, gMethod, 1) < 0) {
            ALOG("RegisterNatives","err");
            return false;
        }
    }

    DetachCurrent();
    return true;
}

int Hook(){
    init();
    void* handle = dlopen("/data/local/libTest.so",RTLD_NOW);
    const char *dlopen_error = dlerror();
    if(!handle){
        ALOG("Error","cannt load plugin :%s",dlopen_error);
        return -1;
    }
    SetupFunc setup = (SetupFunc)dlsym(handle,"getpHookInfo");
    const char *dlsym_error = dlerror();
    if (dlsym_error) {
        ALOG("Error","Cannot load symbol 'getpHookInfo' :%s" , dlsym_error);
        dlclose(handle);
        return 1;
    }

    HookInfo *hookInfo;
    setup(&hookInfo);

    ALOG("LOG","Target Class:%s",hookInfo[0].tClazz);
    ALOG("LOG","Target Method:%s",hookInfo[0].tMethod);

    ClassMethodHook(hookInfo[0]);
}


以下是我们想要的目标进程java世界执行的我们自定义的代码
libTest.so
#include "native.h"
#include <android/log.h>
#include "stdio.h"
#include "stdlib.h"
#include "MethodHooker.h"

#define log(a,b) __android_log_print(ANDROID_LOG_VERBOSE,a,b); // LOG Ѝ:info
#define log_(b) __android_log_print(ANDROID_LOG_VERBOSE,"JNI_LOG_INFO",b); // LOG Ѝ:info

int getpHookInfo(HookInfo** pInfo);

JNIEXPORT void JNICALL Java_com_example_testar_InjectClassloader_hookMethodNative
  (JNIEnv * jenv, jobject jboj, jobject jobj, jclass jclazz, jint slot)
{
    //log("TestAE","start Inject other process");
}

JNIEXPORT jstring JNICALL test(JNIEnv *env, jclass clazz)  
{  
    //__android_log_print(ANDROID_LOG_VERBOSE, "tag", "call <native_printf> in java");
    return (*env)->NewStringUTF(env,"haha ");;
}

HookInfo hookInfos[] = {
        {"android/net/wifi/WifiInfo","getMacAddress","()Ljava/lang/String;",(void*)test},
        //{"com/example/testar/MainActivity","test","()Ljava/lang/String;",(void*)test},
        //{"android/app/ApplicationLoaders","getText","()Ljava/lang/CharSequence;",(void*)test},
};

int getpHookInfo(HookInfo** pInfo){
    *pInfo = hookInfos;
    return sizeof(hookInfos) / sizeof(hookInfos[0]);
}


程序大致的流程是这样的,首先将so.so注入到目标进程,执行里面的Hook()函数,然后Hook()加载libTest.so,获取里面定义的Hook信息.接着用ClassMethodHook挂钩java世界的方法.

        关键一,从native世界进入java世界.熟悉jni编程的都知道,java到native的桥梁是JNIEnv,我们只要获得一个JNIEnv就能进入到java世界了.突破点就在AndroidRuntime,android::AndroidRuntime::getJavaVM();这个静态方法能够获取一个JavaVM, JavaVM->GetEnv方法能够获得一个JNIEnv了.JNIEnv是和线程相关的,使用前一定记得将其附加到当前进程,也要在适当的时候将其销毁.

        关键二,怎么影响内存里的java代码,这个情况替换内存是不现实的,但是可以取巧.我们知道java代码里将一个方法声明为native方法时,对此函数的调用就会到native世界里找.我们何不在运行时将一个不是native的方法修改成native方法呢?这是可以做到的,看着段代码
bool HookDalvikMethod(jmethodID jmethod){
    Method *method = (Method*)jmethod;
    //ؼ!!Ŀ귽ОĎnative
    SET_METHOD_FLAG(method, ACC_NATIVE);

    int argsSize = dvmComputeMethodArgsSize(method);
    if (!dvmIsStaticMethod(method))
        argsSize++;

    method->registersSize = method->insSize = argsSize;

    if (dvmIsNativeMethod(method)) {
        method->nativeFunc = dvmResolveNativeMethod;
        method->jniArgInfo = computeJniArgInfo(&method->prototype);
    }
}


Jni反射调用java方法时要用到一个jmethodID指针,这个指针在Dalvik里其实就是Method类,通过修改这个类的一些属性就可以实现在运行时将一个方法修改成native方法.
SET_METHOD_FLAG(method, ACC_NATIVE); 就是这么做的,其后面的代码就是设定native函数的参数占用内存大小统计.

        也许你发现了,虽然将其修改成一个native方法了,但是这个方法对应的native代码在那里呢?这样做
  //ؼ!!Ŀ귽؁הҥĮative
        if (jenv->RegisterNatives(clazzTarget, gMethod, 1) < 0) {
            ALOG("RegisterNatives","err");
            return false;
        }


可以将一个native函数绑定到一个java的native方法

        这样就能够实现开始的目标了!

        我这里讲得是有点粗略了,但是这个技术牵涉到的知识太多了,主要是给老鸟参考的,小菜们拿来用用就好,要是要讲得小菜们都能明白,就不知要讲到何年何月了.还有就是上面的art环境的代码是跑不起来的,等我后面有空完善了再发一次吧!

        本教程仅供学习交流用途,请勿用于非法用途!

        希望老鸟勿笑,小鸟勿喷!谢谢观赏!

        测试代码猛击这里:http://pan.baidu.com/s/1nt9GBsX

注:本帖由看雪论坛志愿者PEstone 重新将DOC整理排版,若和原文有出入,以原作者附件为准

//###############  14.16.17更新  ##########################
之前没有提到调用原来的方法的方法,方法是这样,直接内存拷贝存起来
	uint mlen = sizeof(Method);
	Method *oldMeth = (Method*)malloc(mlen);
	memcpy(oldMeth,method,mlen);
	info->odlMethod = oldMeth;
	info->curMethod = method;

调用原来的方法就把内存拷回去,调用后再hook一次
	memcpy(hi->curMethod,hi->odlMethod,mlen);

	jmethodID om = (jmethodID)hi->curMethod;
	jenv->CallVoidMethod(me,om,gDevice_Sensors);

	ClassMethodHook(jenv,&baiduhookInfos[0]);

以前觉得这样子可能会有问题,但是经过这么多天使用,而且问的人比较多,感觉还可以,在这里补充一下.

推荐:论坛大聚会| 看雪安全开发者峰会将于7月21号火热来袭!

上传的附件:
最新回复 (158)
Nermor 2014-3-28 14:29
2
越来越好玩了
OnlyEnd 2014-3-28 15:30
3
mark  学习下
fazhho 2014-3-28 15:43
4
原来可以用applicationloader加载类之后再替换方法,以前没想到这步,学习了。
Claud 2014-3-28 16:45
5
赞分享。
在ddi和substrate中就是使用了这个方法实现hook框架。
蟑螂一号 2014-3-28 16:52
6
楼主威武。
starrysky 2014-3-28 17:12
7
学习了,感觉就是用自己的函数通过RegisterNatives替换掉原始的函数,google这个应该可以容易防,只要判断原始的函数有没有,要是有,就不让你注册成功应该就可以防住此类的hook法
bunnyrene 2014-3-28 20:49
8
楼主威武
malokch 2014-3-29 01:55
9
惭愧惭愧,实在是没有艺术细胞,不会在帖子上排版,感谢PEstone帮忙排版
tihty 2014-3-29 08:34
10
学习,感谢楼主分享
malokch 2014-3-29 11:22
11
这个Hook方案是动态可行的,也就是说要检测其合法性,就得实时监测,这个开销还是蛮大的。况且它有检测依据,应该就能去掉其监测依据,只要java方法跳转时其停下来进行native java method判断,而不像art里是连接起来的。
我是土匪 2014-3-29 19:24
12
cydia
https://play.google.com/store/apps/details?id=com.saurik.substrate
IamHuskar 2014-3-30 10:23
13
先标记一下MARK
vvLinker 2014-3-31 09:03
14
支持一个
appview 2014-3-31 10:06
15
谢谢分享,很喜欢,收藏了!
wfgfw 2014-3-31 14:02
16
楼主分析的很好,之前看xposed框架,貌似用的也是这种方法~
ReturnsMe 2014-3-31 14:58
17
这个老鸟已经玩烂了。。。

楼主可以搞下ART试试,dalvik不会活跃太久了~
小龙飞 2014-3-31 15:01
18
顶起 感谢楼主分享
VeryCool 2014-3-31 15:37
19
这个好高深啊,mark,学习
hkzlq 2014-3-31 15:50
20
mark,收藏了。
malokch 2014-3-31 17:14
21
art机制不一样,实现起来不简单
brantb张 2014-3-31 20:16
22
HookArtMethod这个方法在哪,是干什么的
是否有完整的代码
kxzb 2014-4-1 00:09
23
以后移动设备会火啊
华仔在吗 2014-4-1 10:39
24
不错的帖子,感谢分享
ReturnsMe 2014-4-1 11:17
25
实现其实不难,但是熟悉ART确实需要两三周来研究了~

64位会很快流行起来,到时候就不会再有dalvik了~
malokch 2014-4-1 15:23
26
HookArtMethod在art.cpp里面,预想中是用来支持art的,现在是不可用的
malokch 2014-4-1 15:39
27
我研究art也有一两个月了,源码也看得差不多了,真没发现能HOOK一个非native java method!
这也是这篇文章的起因,dalvik虽然出来没几年,但也显得老态龙钟了.先不说64位,现在移动处理能力暴涨,但是这个增长趋势也是有个限度的,当不能从硬件上提升体验,那时候就是转向系统的优化,而dalvik就是个瓶颈,纯粹的dalvik是会被抛弃的.但完全的抛弃应该是不会有的,dalvik就是安卓的核心所在,被彻底抛弃那就安卓就不叫安卓了,除非谷歌真的够魄力整个给重做了,况且技术层面上说,art只是dalvik的另外一种实现方式.至于64位在手持设备的意义不是很大,手持设备终将不会成为大数据处理的载体,64位的地址扩展带来的内存容量提升可能比较有意思一点.
zrhai 2014-4-1 19:48
28
看了一遍,好多地方还是没明白,收藏学习了!
aimer 2014-4-1 21:32
29
不错的教程  感谢LZ的分享
wuaiwu 2014-4-2 10:20
30
标记下,刘明
duoniduoni 2014-4-2 11:26
31
拜谢!!!!
duoniduoni 2014-4-2 11:51
32
请教楼主,hook到java方法后,如何在hook中调用原来方法????
duoniduoni 2014-4-3 09:15
33
lz你好,我想请教一个问题:

你代码中用到的class Method等类和dvXXXX系列函数,是dalivk虚拟机中提供的吧?这些类、方法如何学习?有没有什么资料系统的讲这些接口的(难道这是一套SDK?)?

希望lz有空的时候回复我的问题。拜谢!
不歪 2014-4-3 10:09
34
非常感谢你的分享
malokch 2014-4-3 13:52
35
这个我也没有找到一个很完美的方法,但是还是有一个方法。就是用“递归”的方式进入原来的方法:先Hook java method,当执行序列到我们注入的代码的时候,先做完你的坏事,然后unhook这个方法,再“递归”调用自身,返回的时候再Hook它。
malokch 2014-4-3 14:07
36
你去下我提供的测试代码就知道我是怎么引用的了,是直接从虚拟机源码里引用的.
至于描述这些方法接口的资料,系统的是没有的,都是网上一些零散的描述.这毕竟只是用于实现系统内部功能的,所以没有什么公开资料,真想学习的话,就去读它的源码和注释,对于开源项目来说,源码和注释是最好的学习资料了.
别看着虚拟机源码有几十兆,它的核心部分没那么庞大.多留意一下,利用一下辅助工具,还是很容易看到你想看到的东西的
duoniduoni 2014-4-3 17:11
37
感谢,你的代码我已经下载了,我以前都是做的本地层拦截,这块一直想搞没开始,感谢回复,我有问题了再请教你
笨小孩xlz 2014-4-5 23:07
38
谢谢分享,很喜欢,收藏了!
zhangsj 2014-4-6 15:03
39
感谢分享 不错不错 收藏了
duoniduoni 2014-4-8 20:20
40
又来麻烦lz,先拜谢。

我准备学习dalvik虚拟机源码,准备做android java 方法拦截。我想请问一下,我应该看虚拟机源码哪一部分,如何学习,有没有啥比较好的教程之类的,毕竟源码还是很大的-_-!。请lz多多指点,拜谢啊拜谢。

祝工作顺利。
malokch 2014-4-9 11:36
41
大体的方法是:找到切入点,然后顺藤摸瓜!
既然是做方法拦截的话,那就是看dalvik字节码执行的部分,这部分的代码都在/dalvik/vm下,具体的你看下目录结构就清楚了。基本每个文件名都表达了其意义。
hqdvista 2014-4-9 17:18
42
不错,学习了
duoniduoni 2014-4-9 18:49
43
感谢!拜谢!
donttellme 2014-4-13 00:32
44
看完标题 就猜到 肯定是用反射 在c/c++中调用java,一般软件开发中也会用到这种方法,主要是当处理数据的方法用C/C++完成,需要在java代码中显示进度条之类的操作 却没有数据,于是在C/c++中去反射获取方法,再调用自身的数据去执行相应操作。
malokch 2014-4-13 01:10
45
这篇文章可不是用来介绍jni和反射的,就那点事也不好意思发这里。这篇文章的核心是HOOK,反射只是用来找到相应方法而已,还有一个要点是在没有java主动支持的情况下从native进入java世界,寻找虚拟机的实例也是个难点。
tonyking 2014-4-22 10:50
46
谢谢分享
IBMLover 2014-4-22 11:10
47
表示虚拟机里测试api8,api16均无法通过,process直接stopped的!还有人遇到同样的情况阿??
小悠 2014-4-23 00:13
48
mark
obi王 2014-4-23 11:18
49
这个要mark一下啊。感谢楼主
luyikk 2014-4-24 14:59
50
....无法OPEN DLL~
返回