hook java android_Android Hook Java的的一個改進版本
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(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等等。
總結
以上是生活随笔為你收集整理的hook java android_Android Hook Java的的一個改進版本的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 调色板原理
- 下一篇: Android 开发神器系列(工具篇)之