注入安卓进程,并hook java世界的方法
生活随笔
收集整理的這篇文章主要介紹了
注入安卓进程,并hook java世界的方法
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
說明:
安卓系統的可執行對象有兩個世界,一個是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>();@Overrideprotected?void?onCreate(Bundle?savedInstanceState)?{super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button?btn?=?(Button)?findViewById(R.id.button1);btn.setOnClickListener(new?View.OnClickListener()?{@Overridepublic?void?onClick(View?v)?{//?TODO?Auto-generated?method?stubWifiManager?wifi?=?(WifiManager)?getSystemService(Context.WIFI_SERVICE);WifiInfo?info?=?wifi.getConnectionInfo();System.out.println("Wifi?mac?:"?+?info.getMacAddress());System.out.println("return?"?+?test());}});}@Overridepublic?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?");; }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?<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){//������oadersjclass?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??aluesjclass?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;//?!!??О?nativeSET_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){//?!!??????ativeif?(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??:infoint?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;//?!!??О?nativeSET_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代碼在那里呢?這樣做
代碼: ??//?!!??????ativeif?(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]);
安卓系統的可執行對象有兩個世界,一個是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>();@Overrideprotected?void?onCreate(Bundle?savedInstanceState)?{super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button?btn?=?(Button)?findViewById(R.id.button1);btn.setOnClickListener(new?View.OnClickListener()?{@Overridepublic?void?onClick(View?v)?{//?TODO?Auto-generated?method?stubWifiManager?wifi?=?(WifiManager)?getSystemService(Context.WIFI_SERVICE);WifiInfo?info?=?wifi.getConnectionInfo();System.out.println("Wifi?mac?:"?+?info.getMacAddress());System.out.println("return?"?+?test());}});}@Overridepublic?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?");; }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?<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){//������oadersjclass?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??aluesjclass?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;//?!!??О?nativeSET_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){//?!!??????ativeif?(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??:infoint?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;//?!!??О?nativeSET_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代碼在那里呢?這樣做
代碼: ??//?!!??????ativeif?(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]);
總結
以上是生活随笔為你收集整理的注入安卓进程,并hook java世界的方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于Android的ELF PLT/GO
- 下一篇: Android Hook Java的的一