【原】http://bbs.pediy.com/showthread.php?t=186054
说明:
安卓系统的可执行对象有两个世界,一个是Linux Native世界,一个是Java世界.两个世界能够通过jvm产生交互,具体来说就是通过jni技术进行互相干涉.但是在正常情况下,只能在Java世界通过jni调用native方法,二native不能在没有任何java上的支持下干涉java世界.
在一些应用中,我们需要对一个app的java世界进行干涉.再说到linux上的进程注入技术,已不用我多讲,但是传统的linux进程注入技术在安卓上只能进入目标进程的native世界.
于是本教程是要注入别的进程,并hook java世界的java 方法!
文章长,详情见附件
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 MapmLoaders = new HashMap (); @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"; } }
so.cpp: #include "jni.h" #include "android_runtime/AndroidRuntime.h" #include "android/log.h" #include "stdio.h" #include "stdlib.h" #include "MethodHooker.h" #include#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 ");; } MethodHooker.cpp: #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 #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]); }
libTest.so #include "native.h" #include#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 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]); }
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); } }
//ؼ!!Ŀ귽הҥĮative if (jenv->RegisterNatives(clazzTarget, gMethod, 1) < 0) { ALOG("RegisterNatives","err"); return false; }