Android(安卓)Hook Java的的一个改进版本
目录(?)[-]
- Hook Java的的一个改进版本
- 改进点一更简单地修改java方法为本地方法
- 改进点二方法回调避免线程安全问题
- 最后
《注入安卓进程,并Hook java世界的方法》这篇好文相信大家都看这,里面所提到的方法估计大家也都试过。不过里面的所用的方法,我发现有两个可以改进的地方。
改进点一:更简单地修改java方法为本地方法
[cpp] view plain copy- ...
- // 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可以是下面两种形式之一:
[cpp] view plain copy- 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。
改进点二:方法回调避免线程安全问题
原来的方法,是这样的
[cpp] view plain copy- //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,这个函数的原型是这样的:
[cpp] view plain copy- 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只能自己写代码了,下面是我的代码:
[cpp] view plain copy- 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里头了:
[cpp] view plain copy- 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,不过这部分逻辑我没有做上,有兴趣的朋友可以加上。
[cpp] view plain copy- 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等等。
原文地址: http://blog.csdn.net/l173864930/article/details/39667355
更多相关文章
- Android之调用百度地图API规划当前位置到指定位置的路线
- Android系统启动阶段多种快速重启系统方法试验记录
- AsyncTask的使用和原理探究(一)
- Android(安卓)Studio apk打包,keystore.jks文件生成,根据keystore
- Android(安卓)View相关-事件分发机制详解-View
- 如何通过看google工程师的提交记录获取第一手Android资料
- Android蓝牙播放如何显示歌曲信息?
- 解锁Retrofit -- 浅析Retrofit源码
- Android如何判断当前手机是否正在播放音乐,并获取到正在播放的音