/* * Get an array with all constructors declared by a class. */ ArrayObject* dvmGetDeclaredConstructors(ClassObject* clazz, bool publicOnly) { if (!dvmIsClassInitialized(gDvm.classJavaLangReflectConstructor)) dvmInitClass(gDvm.classJavaLangReflectConstructor); /* * Ordinarily we init the class the first time we resolve a method. * We're bypassing the normal resolution mechanism, so we init it here. */ if (!dvmIsClassInitialized(clazz)) dvmInitClass(clazz); /* * Count up the #of relevant methods. */ size_t count = 0; for (int i = 0; i < clazz->directMethodCount; ++i) { Method* meth = &clazz->directMethods[i]; if ((!publicOnly || dvmIsPublicMethod(meth)) && dvmIsConstructorMethod(meth) && !dvmIsStaticMethod(meth)) { count++; } } /* * Create an array of Constructor objects. */ ClassObject* arrayClass = gDvm.classJavaLangReflectConstructorArray; ArrayObject* ctorArray = dvmAllocArrayByClass(arrayClass, count, ALLOC_DEFAULT); if (ctorArray == NULL) return NULL; /* * Fill out the array. */ size_t ctorObjCount = 0; for (int i = 0; i < clazz->directMethodCount; ++i) { Method* meth = &clazz->directMethods[i]; if ((!publicOnly || dvmIsPublicMethod(meth)) && dvmIsConstructorMethod(meth) && !dvmIsStaticMethod(meth)) { Object* ctorObj = createConstructorObject(meth); if (ctorObj == NULL) { dvmReleaseTrackedAlloc((Object*) ctorArray, NULL); return NULL; } dvmSetObjectArrayElement(ctorArray, ctorObjCount, ctorObj); ++ctorObjCount; dvmReleaseTrackedAlloc(ctorObj, NULL); } } assert(ctorObjCount == ctorArray->length); /* caller must call dvmReleaseTrackedAlloc */ return ctorArray; }
u4 getEntryTaint(const char* entry, const u4* args, const Method* method) { u4 tag = TAINT_CLEAR; char *pos; /* Determine split point if any */ pos = index((char *) entry, '.'); switch (getEntryType(entry)) { case kTaintProfileClass: if (dvmIsStaticMethod(method)) { tag = getFieldEntryTaint(pos+1, method->clazz, NULL); } else { tag = getFieldEntryTaint(pos+1, method->clazz, (Object*)args[0]); } break; case kTaintProfileParam: tag = getParamEntryTaint(entry, args, method); break; default: ALOGW("TaintPolicy: Invalid from type: [%s]", entry); } return tag; }
u4 addEntryTaint(u4 tag, const char* entry, const u4* args, const Method* method) { u4 rtaint = TAINT_CLEAR; switch (getEntryType(entry)) { case kTaintProfileClass: if (dvmIsStaticMethod(method)) { addFieldEntryTaint(tag, entry, method->clazz, NULL); } else { addFieldEntryTaint(tag, entry, method->clazz, (Object*)args[0]); } break; case kTaintProfileParam: addParamEntryTaint(tag, entry, args, method); break; case kTaintProfileReturn: if (entry[7] == '\0') { /* taint the return itself */ rtaint = tag; } else { // TODO: implement return field tainting (need pResult) ALOGW("TaintPolicy: tainting return fields not supported"); } break; default: ALOGW("TaintPolicy: Invalid from type: [%s]", entry); } return rtaint; }
/* * Resolve a native method and invoke it. * * This is executed as if it were a native bridge or function. If the * resolution succeeds, method->insns is replaced, and we don't go through * here again. * * Initializes method's class if necessary. * * An exception is thrown on resolution failure. */ void dvmResolveNativeMethod(const u4* args, JValue* pResult, const Method* method, Thread* self) { ClassObject* clazz = method->clazz; void* func; /* * If this is a static method, it could be called before the class * has been initialized. */ if (dvmIsStaticMethod(method)) { if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) { assert(dvmCheckException(dvmThreadSelf())); return; } } else { assert(dvmIsClassInitialized(clazz) || dvmIsClassInitializing(clazz)); } /* start with our internal-native methods */ func = dvmLookupInternalNativeMethod(method); if (func != NULL) { /* resolution always gets the same answer, so no race here */ IF_LOGVV() { char* desc = dexProtoCopyMethodDescriptor(&method->prototype); LOGVV("+++ resolved native %s.%s %s, invoking\n", clazz->descriptor, method->name, desc); free(desc); }
/* * 在'clazz'中,我们有一个方法指向另一个方法,但它可能指向派生类中的一个方法,我们想要从虚拟函数表中找到实际入口,如果'class'是一个接口,我们不得不在短时间内做更多的挖掘 * * 对于'direct'方法(private / constructor).我们仅仅返回原生态方法 * * (这里被适用于反射和JNI调用) */ const Method* dvmGetVirtualizedMethod(const ClassObject* clazz, const Method* meth) { Method* actualMeth; int methodIndex; if (dvmIsDirectMethod(meth)) { /* no vtable entry for these */ assert(!dvmIsStaticMethod(meth)); return meth; } /* * If the method was declared in an interface, we need to scan through * the class' list of interfaces for it, and find the vtable index * from that. * * TODO: use the interface cache. */ if (dvmIsInterfaceClass(meth->clazz)) { int i; for (i = 0; i < clazz->iftableCount; i++) { if (clazz->iftable[i].clazz == meth->clazz) break; } if (i == clazz->iftableCount) { dvmThrowIncompatibleClassChangeError( "invoking method from interface not implemented by class"); return NULL; } methodIndex = clazz->iftable[i].methodIndexArray[meth->methodIndex]; } else { methodIndex = meth->methodIndex; } assert(methodIndex >= 0 && methodIndex < clazz->vtableCount); actualMeth = clazz->vtable[methodIndex]; /* * Make sure there's code to execute. */ if (dvmIsAbstractMethod(actualMeth)) { dvmThrowAbstractMethodError(NULL); return NULL; } assert(!dvmIsMirandaMethod(actualMeth)); return actualMeth; }
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); } }
/* Returns the index in args[] corresponding to the parameter * string entry. * - It doesn't matter if the entry has multiple parts, e.g., * "param1.foo.bar", as long as the variable name after the first * "." is not a number. Since the signature comes next, we can * safely assume this is the case. * - returns -1 on parsing error * - If descriptor is not NULL, it will point to a newly allocated * descriptor that needs to be free()'d (unless there was an error) */ int paramToArgIndex(const char* entry, const Method* method, char** descriptor) { int pIdx, aIdx, i; char* endptr; const char* num = entry + 5; /* "param" is the first 5 characters */ const DexProto* proto = &method->prototype; DexParameterIterator pIterator; /* Step 1: determine the parameter index (pIdx) */ pIdx = strtol(num, &endptr, 10); if (num == endptr) { /* error parsing */ return -1; } /* Step 2: translate parameter index into args array index */ dexParameterIteratorInit(&pIterator, proto); aIdx = (dvmIsStaticMethod(method)?0:1); /* index where params start */ for (i=0; i<=pIdx ; i++) { const char* desc = dexParameterIteratorNextDescriptor(&pIterator); if (desc == NULL) { /* This index doesn't exist, error */ return -1; } if (i == pIdx) { /* This is the index */ if (descriptor != NULL) { *descriptor = strdup(desc); } break; } /* increment the args array index */ aIdx++; if (desc[0] == 'J' || desc[0] == 'D') { /* wide argument, increment index one more */ aIdx++; } } return aIdx; }
extern void __attribute__ ((visibility ("hidden"))) dalvik_replaceMethod( JNIEnv* env, jobject src, jobject dest) { jobject clazz = env->CallObjectMethod(dest, jClassMethod); ClassObject* clz = (ClassObject*) dvmDecodeIndirectRef_fnPtr( dvmThreadSelf_fnPtr(), clazz); clz->status = CLASS_INITIALIZED; Method* meth = (Method*) env->FromReflectedMethod(src); Method* target = (Method*) env->FromReflectedMethod(dest); LOGD("dalvikMethod: %s", meth->name); meth->jniArgInfo = 0x80000000; meth->accessFlags |= ACC_NATIVE; int argsSize = dvmComputeMethodArgsSize_fnPtr(meth); if (!dvmIsStaticMethod(meth)) argsSize++; meth->registersSize = meth->insSize = argsSize; meth->insns = (void*) target;//在Dalvik中,每个方法都是一个Method的结构体,其中当这个方法是native的时候,Method的insns这个指针会指向native方法的起始地址。 meth->nativeFunc = dalvik_dispatcher; }
/** * 使用jni GetMethodID 方法获取jmethodID 强制转为 Method 的hook 方法 示例 */ static void newTestMethod(const u4* args, JValue* pResult, const Method* method, struct Thread* self) { // args 是原来函数的参数数组, 原来test函数只有一个String型参数 // 并且要注意, 如果是不是static函数, 下标0 是函数所在类的实例obj // 在dvm中Object, jni 中的jobject 和 java 中的 Object类 都不是同一个东西 // String类对应StringObject // 取出参数打印出来看看 StringObject* param1 = NULL; if(dvmIsStaticMethod(method)) param1 = (StringObject*)args[0]; else param1 = (StringObject*)args[1]; LOGD("param1:%s",dvmCreateCstrFromString(param1)); //JValue 是个union ,要返回int 就 pResult->i=1; 返回Object对象就 pResult->l = ojb; // 但是, 在dvm中的Object, jni 中的jobject 和 java 中的 Object类 都不是同一个东西 // 所以, 我们这里使用dvm的函数来创建一个StringObject* pResult->l = dvmCreateStringFromCstr("newTestMethod"); // 一般情况应该使用宏 : RETURN_XXX(result); return; }
/* * Issue a method call with arguments provided in an array. We process * the contents of "args" by scanning the method signature. * * The values were likely placed into an uninitialized jvalue array using * the field specifiers, which means that sub-32-bit fields (e.g. short, * boolean) may not have 32 or 64 bits of valid data. This is different * from the varargs invocation where the C compiler does a widening * conversion when calling a function. As a result, we have to be a * little more precise when pulling stuff out. * * "args" may be NULL if the method has no arguments. */ void dvmCallMethodA(Thread* self, const Method* method, Object* obj, bool fromJni, JValue* pResult, const jvalue* args) { //salma // __android_log_print(ANDROID_LOG_DEBUG, "DVM DEBUG", "dvmCallMethodA method name = %s, clazz name: %s", method->name, method->clazz->descriptor); //end salma const char* desc = &(method->shorty[1]); // [0] is the return type. int verifyCount = 0; ClassObject* clazz; u4* ins; clazz = callPrep(self, method, obj, false); if (clazz == NULL) return; /* "ins" for new frame start at frame pointer plus locals */ ins = ((u4*)self->interpSave.curFrame) + (method->registersSize - method->insSize); /* put "this" pointer into in0 if appropriate */ if (!dvmIsStaticMethod(method)) { assert(obj != NULL); *ins++ = (u4) obj; /* obj is a "real" ref */ verifyCount++; } while (*desc != '\0') { switch (*desc++) { case 'D': /* 64-bit quantity; have to use */ case 'J': /* memcpy() in case of mis-alignment */ memcpy(ins, &args->j, 8); ins += 2; verifyCount++; /* this needs an extra push */ break; case 'L': /* includes array refs */ if (fromJni) *ins++ = (u4) dvmDecodeIndirectRef(self, args->l); else *ins++ = (u4) args->l; break; case 'F': case 'I': *ins++ = args->i; /* full 32 bits */ break; case 'S': *ins++ = args->s; /* 16 bits, sign-extended */ break; case 'C': *ins++ = args->c; /* 16 bits, unsigned */ break; case 'B': *ins++ = args->b; /* 8 bits, sign-extended */ break; case 'Z': *ins++ = args->z; /* 8 bits, zero or non-zero */ break; default: ALOGE("Invalid char %c in short signature of %s.%s", *(desc-1), clazz->descriptor, method->name); assert(false); goto bail; } verifyCount++; args++; } #ifndef NDEBUG if (verifyCount != method->insSize) { ALOGE("Got vfycount=%d insSize=%d for %s.%s", verifyCount, method->insSize, clazz->descriptor, method->name); assert(false); goto bail; } #endif if (dvmIsNativeMethod(method)) { TRACE_METHOD_ENTER(self, method); /* * Because we leave no space for local variables, "curFrame" points * directly at the method arguments. */ (*method->nativeFunc)((u4*)self->interpSave.curFrame, pResult, method, self); TRACE_METHOD_EXIT(self, method); } else { dvmInterpret(self, method, pResult); } bail: dvmPopFrame(self); }
/* * Issue a method call with a variable number of arguments. We process * the contents of "args" by scanning the method signature. * * Pass in NULL for "obj" on calls to static methods. * * We don't need to take the class as an argument because, in Dalvik, * we don't need to worry about static synchronized methods. */ void dvmCallMethodV(Thread* self, const Method* method, Object* obj, bool fromJni, JValue* pResult, va_list args) { //salma //__android_log_print(ANDROID_LOG_DEBUG, "DVM DEBUG", "dvmCallMethodV method name = %s, clazz name: %s", method->name, method->clazz->descriptor); //end salma const char* desc = &(method->shorty[1]); // [0] is the return type. int verifyCount = 0; ClassObject* clazz; u4* ins; clazz = callPrep(self, method, obj, false); if (clazz == NULL) return; /* "ins" for new frame start at frame pointer plus locals */ ins = ((u4*)self->interpSave.curFrame) + (method->registersSize - method->insSize); //ALOGD(" FP is %p, INs live at >= %p", self->interpSave.curFrame, ins); /* put "this" pointer into in0 if appropriate */ if (!dvmIsStaticMethod(method)) { #ifdef WITH_EXTRA_OBJECT_VALIDATION assert(obj != NULL && dvmIsHeapAddress(obj)); #endif *ins++ = (u4) obj; verifyCount++; } while (*desc != '\0') { switch (*(desc++)) { case 'D': case 'J': { u8 val = va_arg(args, u8); memcpy(ins, &val, 8); // EABI prevents direct store ins += 2; verifyCount += 2; break; } case 'F': { /* floats were normalized to doubles; convert back */ float f = (float) va_arg(args, double); *ins++ = dvmFloatToU4(f); verifyCount++; break; } case 'L': { /* 'shorty' descr uses L for all refs, incl array */ void* arg = va_arg(args, void*); assert(obj == NULL || dvmIsHeapAddress(obj)); jobject argObj = reinterpret_cast<jobject>(arg); if (fromJni) *ins++ = (u4) dvmDecodeIndirectRef(self, argObj); else *ins++ = (u4) argObj; verifyCount++; break; } default: { /* Z B C S I -- all passed as 32-bit integers */ *ins++ = va_arg(args, u4); verifyCount++; break; } } } #ifndef NDEBUG if (verifyCount != method->insSize) { ALOGE("Got vfycount=%d insSize=%d for %s.%s", verifyCount, method->insSize, clazz->descriptor, method->name); assert(false); goto bail; } #endif //dvmDumpThreadStack(dvmThreadSelf()); if (dvmIsNativeMethod(method)) { TRACE_METHOD_ENTER(self, method); /* * Because we leave no space for local variables, "curFrame" points * directly at the method arguments. */ (*method->nativeFunc)((u4*)self->interpSave.curFrame, pResult, method, self); TRACE_METHOD_EXIT(self, method); } else { dvmInterpret(self, method, pResult); } #ifndef NDEBUG bail: #endif dvmPopFrame(self); }
/* Used to propagate taint for JNI methods * Two types of propagation: * 1) simple conservative propagation based on parameters * 2) propagation based on function profile policies */ void dvmTaintPropJniMethod(const u4* args, JValue* pResult, const Method* method, const int nativeTaint) { const DexProto* proto = &method->prototype; DexParameterIterator pIterator; int nParams = dexProtoGetParameterCount(proto); int pStart = (dvmIsStaticMethod(method)?0:1); /* index where params start */ /* Consider 3 arguments. [x] indicates return taint index * 0 1 2 [3] 4 5 6 */ int nArgs = method->insSize; u4* rtaint = (u4*) &args[nArgs]; /* The return taint value */ int tStart = nArgs+1; /* index of args[] where taint values start */ int tEnd = nArgs*2; /* index of args[] where taint values end */ u4 tag = TAINT_CLEAR; int i; #if 0 { char *desc = dexProtoCopyMethodDescriptor(proto); ALOGW("Jni: %s.%s%s, descriptor: %s", method->clazz->descriptor, method->name, dvmIsStaticMethod(method)?"[static]":"", desc); free(desc); } #endif #ifdef TAINT_JNI_LOG /* JNI logging for debugging purposes */ if (gJniLog) { u4 hash; int len; char *inStr, *outStr; len = strlen(method->clazz->descriptor) + 1 + strlen(method->name); inStr = (char*) malloc(len+1); strcpy(inStr, method->clazz->descriptor); strcat(inStr, "."); strcat(inStr, method->name); hash = dvmComputeUtf8Hash(inStr); dvmHashTableLock(gJniLogSeen); outStr = (char*) dvmHashTableLookup(gJniLogSeen, hash, inStr, (HashCompareFunc) strcmp, false); if (outStr == NULL) { /* New method! */ /* add it */ dvmHashTableLookup(gJniLogSeen, hash, inStr, (HashCompareFunc) strcmp, true); } else { free(inStr); /* don't need this anymore */ } dvmHashTableUnlock(gJniLogSeen); } #endif /* Union the taint tags, this includes object ref tags * - we don't need to worry about static vs. not static, because getting * the taint tag on the "this" object reference is a good * - we don't need to worry about wide registers, because the stack * interleaving of taint tags makes it transparent */ /*for (i = tStart; i <= tEnd; i++) { tag |= args[i]; if (args[i]!=0 && args[i]!=TAINT_ACCELEROMETER) //found a taint, and its not the accelerometer spam either ALOGD("dvmTaintPropJNIMethod found taint %08x in method=%s:%s(%s) for arg[%d]=%08x", args[i], method->clazz->descriptor, method->name, method->shorty, i-nArgs, args[i-nArgs]); }*/ /* If not static, pull any taint from the "this" object */ /*if (!dvmIsStaticMethod(method)) { tag |= getObjectTaint((Object*)args[0], method->clazz->descriptor); }*/ /* Union taint from Objects we care about */ dexParameterIteratorInit(&pIterator, proto); for (i=pStart; ; i++) { const char* desc = dexParameterIteratorNextDescriptor(&pIterator); if (desc == NULL) { break; } if (desc[0] == '[' || desc[0] == 'L') { tag |= getObjectTaint((Object*) args[i], desc); } if (desc[0] == 'J' || desc[0] == 'D') { /* wide argument, increment index one more */ i++; } } /* Look at the taint policy profiles (may have return taint) */ //tag |= propMethodProfile(args, method); /* Update return taint according to the return type */ //Native Tainting: get taint Value from native code //if (tag) { if (nativeTaint) { const char* desc = dexProtoGetReturnType(proto); //setReturnTaint(tag, rtaint, pResult, desc); ALOGD("dvmTaintPropJniMethod: setting result taint to %x", nativeTaint); setReturnTaint(nativeTaint, rtaint, pResult, desc); } }
/* Dalvik puts private, static, and constructors into non-virtual table */ INLINE bool dvmIsDirectMethod(const Method* method) { return dvmIsPrivateMethod(method) || dvmIsStaticMethod(method) || dvmIsConstructorMethod(method); }
/* * Issue a method call with a variable number of arguments. We process * the contents of "args" by scanning the method signature. * * Pass in NULL for "obj" on calls to static methods. * * We don't need to take the class as an argument because, in Dalvik, * we don't need to worry about static synchronized methods. */ void dvmCallMethodV(Thread* self, const Method* method, Object* obj, JValue* pResult, va_list args) { const char* desc = &(method->shorty[1]); // [0] is the return type. int verifyCount = 0; ClassObject* clazz; u4* ins; clazz = callPrep(self, method, obj, false); if (clazz == NULL) return; /* "ins" for new frame start at frame pointer plus locals */ ins = ((u4*)self->curFrame) + (method->registersSize - method->insSize); //LOGD(" FP is %p, INs live at >= %p\n", self->curFrame, ins); /* put "this" pointer into in0 if appropriate */ if (!dvmIsStaticMethod(method)) { #ifdef WITH_EXTRA_OBJECT_VALIDATION assert(obj != NULL && dvmIsValidObject(obj)); #endif *ins++ = (u4) obj; verifyCount++; } while (*desc != '\0') { switch (*(desc++)) { case 'D': case 'J': { u8 val = va_arg(args, u8); memcpy(ins, &val, 8); // EABI prevents direct store ins += 2; verifyCount += 2; break; } case 'F': { /* floats were normalized to doubles; convert back */ float f = (float) va_arg(args, double); *ins++ = dvmFloatToU4(f); verifyCount++; break; } #ifdef WITH_EXTRA_OBJECT_VALIDATION case 'L': { /* 'shorty' descr uses L for all refs, incl array */ Object* argObj = (Object*) va_arg(args, u4); assert(obj == NULL || dvmIsValidObject(obj)); *ins++ = (u4) argObj; verifyCount++; break; } #endif default: { *ins++ = va_arg(args, u4); verifyCount++; break; } } } #ifndef NDEBUG if (verifyCount != method->insSize) { LOGE("Got vfycount=%d insSize=%d for %s.%s\n", verifyCount, method->insSize, clazz->descriptor, method->name); assert(false); goto bail; } #endif //dvmDumpThreadStack(dvmThreadSelf()); if (dvmIsNativeMethod(method)) { /* * Because we leave no space for local variables, "curFrame" points * directly at the method arguments. */ (*method->nativeFunc)(self->curFrame, pResult, method, self); } else { dvmInterpret(self, method, pResult); } bail: dvmPopFrame(self); }
/* * Issue a method call with arguments provided in an array. We process * the contents of "args" by scanning the method signature. * * The values were likely placed into an uninitialized jvalue array using * the field specifiers, which means that sub-32-bit fields (e.g. short, * boolean) may not have 32 or 64 bits of valid data. This is different * from the varargs invocation where the C compiler does a widening * conversion when calling a function. As a result, we have to be a * little more precise when pulling stuff out. * * "args" may be NULL if the method has no arguments. */ void dvmCallMethodA(Thread* self, const Method* method, Object* obj, JValue* pResult, const jvalue* args) { const char* desc = &(method->shorty[1]); // [0] is the return type. int verifyCount = 0; ClassObject* clazz; u4* ins; clazz = callPrep(self, method, obj, false); if (clazz == NULL) return; /* "ins" for new frame start at frame pointer plus locals */ ins = ((u4*)self->curFrame) + (method->registersSize - method->insSize); /* put "this" pointer into in0 if appropriate */ if (!dvmIsStaticMethod(method)) { assert(obj != NULL); *ins++ = (u4) obj; verifyCount++; } while (*desc != '\0') { switch (*(desc++)) { case 'D': case 'J': { memcpy(ins, &args->j, 8); /* EABI prevents direct store */ ins += 2; verifyCount += 2; args++; break; } case 'F': case 'I': case 'L': { /* (no '[' in short signatures) */ *ins++ = args->i; /* get all 32 bits */ verifyCount++; args++; break; } case 'S': { *ins++ = args->s; /* 16 bits, sign-extended */ verifyCount++; args++; break; } case 'C': { *ins++ = args->c; /* 16 bits, unsigned */ verifyCount++; args++; break; } case 'B': { *ins++ = args->b; /* 8 bits, sign-extended */ verifyCount++; args++; break; } case 'Z': { *ins++ = args->z; /* 8 bits, zero or non-zero */ verifyCount++; args++; break; } default: { LOGE("Invalid char %c in short signature of %s.%s\n", *(desc-1), clazz->descriptor, method->name); assert(false); goto bail; } } } #ifndef NDEBUG if (verifyCount != method->insSize) { LOGE("Got vfycount=%d insSize=%d for %s.%s\n", verifyCount, method->insSize, clazz->descriptor, method->name); assert(false); goto bail; } #endif if (dvmIsNativeMethod(method)) { /* * Because we leave no space for local variables, "curFrame" points * directly at the method arguments. */ (*method->nativeFunc)(self->curFrame, pResult, method, self); } else { dvmInterpret(self, method, pResult); } bail: dvmPopFrame(self); }
/* * Alternate version of dvmResolveMethod(). * * Doesn't throw exceptions, and checks access on every lookup. * * On failure, returns NULL, and sets *pFailure if pFailure is not NULL. */ Method* dvmOptResolveMethod(ClassObject* referrer, u4 methodIdx, MethodType methodType, VerifyError* pFailure) { DvmDex* pDvmDex = referrer->pDvmDex; Method* resMethod; assert(methodType == METHOD_DIRECT || methodType == METHOD_VIRTUAL || methodType == METHOD_STATIC); LOGVV("--- resolving method %u (referrer=%s)", methodIdx, referrer->descriptor); resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx); if (resMethod == NULL) { const DexMethodId* pMethodId; ClassObject* resClass; pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx); resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, pFailure); if (resClass == NULL) { /* * Can't find the class that the method is a part of, or don't * have permission to access the class. */ ALOGV("DexOpt: can't find called method's class (?.%s)", dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx)); if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); } return NULL; } if (dvmIsInterfaceClass(resClass)) { /* method is part of an interface; this is wrong method for that */ ALOGW("DexOpt: method is in an interface"); if (pFailure != NULL) *pFailure = VERIFY_ERROR_GENERIC; return NULL; } /* * We need to chase up the class hierarchy to find methods defined * in super-classes. (We only want to check the current class * if we're looking for a constructor.) */ DexProto proto; dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId); if (methodType == METHOD_DIRECT) { resMethod = dvmFindDirectMethod(resClass, dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto); } else { /* METHOD_STATIC or METHOD_VIRTUAL */ resMethod = dvmFindMethodHier(resClass, dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto); } if (resMethod == NULL) { ALOGV("DexOpt: couldn't find method '%s'", dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx)); if (pFailure != NULL) *pFailure = VERIFY_ERROR_NO_METHOD; return NULL; } if (methodType == METHOD_STATIC) { if (!dvmIsStaticMethod(resMethod)) { ALOGD("DexOpt: wanted static, got instance for method %s.%s", resClass->descriptor, resMethod->name); if (pFailure != NULL) *pFailure = VERIFY_ERROR_CLASS_CHANGE; return NULL; } } else if (methodType == METHOD_VIRTUAL) { if (dvmIsStaticMethod(resMethod)) { ALOGD("DexOpt: wanted instance, got static for method %s.%s", resClass->descriptor, resMethod->name); if (pFailure != NULL) *pFailure = VERIFY_ERROR_CLASS_CHANGE; return NULL; } } /* see if this is a pure-abstract method */ if (dvmIsAbstractMethod(resMethod) && !dvmIsAbstractClass(resClass)) { ALOGW("DexOpt: pure-abstract method '%s' in %s", dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), resClass->descriptor); if (pFailure != NULL) *pFailure = VERIFY_ERROR_GENERIC; return NULL; } /* * Add it to the resolved table so we're faster on the next lookup. * * We can only do this for static methods if we're not in "dexopt", * because the presence of a valid value in the resolution table * implies that the class containing the static field has been * initialized. */ if (methodType != METHOD_STATIC || gDvm.optimizing) dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod); } LOGVV("--- found method %d (%s.%s)", methodIdx, resMethod->clazz->descriptor, resMethod->name); /* access allowed? */ tweakLoader(referrer, resMethod->clazz); bool allowed = dvmCheckMethodAccess(referrer, resMethod); untweakLoader(referrer, resMethod->clazz); if (!allowed) { IF_ALOGI() { char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype); ALOGI("DexOpt: illegal method access (call %s.%s %s from %s)", resMethod->clazz->descriptor, resMethod->name, desc, referrer->descriptor); free(desc); }
/* * private Object invokeNative(Object obj, Object[] args, Class declaringClass, * Class[] parameterTypes, Class returnType, int slot, boolean noAccessCheck) * * Invoke a static or virtual method via reflection. */ static void Dalvik_java_lang_reflect_Method_invokeNative(const u4* args, JValue* pResult) { // ignore thisPtr in args[0] Object* methObj = (Object*) args[1]; // null for static methods ArrayObject* argList = (ArrayObject*) args[2]; ClassObject* declaringClass = (ClassObject*) args[3]; ArrayObject* params = (ArrayObject*) args[4]; ClassObject* returnType = (ClassObject*) args[5]; int slot = args[6]; bool noAccessCheck = (args[7] != 0); const Method* meth; Object* result; /* * "If the underlying method is static, the class that declared the * method is initialized if it has not already been initialized." */ meth = dvmSlotToMethod(declaringClass, slot); assert(meth != NULL); if (dvmIsStaticMethod(meth)) { if (!dvmIsClassInitialized(declaringClass)) { if (!dvmInitClass(declaringClass)) goto init_failed; } } else { /* looks like interfaces need this too? */ if (dvmIsInterfaceClass(declaringClass) && !dvmIsClassInitialized(declaringClass)) { if (!dvmInitClass(declaringClass)) goto init_failed; } /* make sure the object is an instance of the expected class */ if (!dvmVerifyObjectInClass(methObj, declaringClass)) { assert(dvmCheckException(dvmThreadSelf())); RETURN_VOID(); } /* do the virtual table lookup for the method */ meth = dvmGetVirtualizedMethod(methObj->clazz, meth); if (meth == NULL) { assert(dvmCheckException(dvmThreadSelf())); RETURN_VOID(); } } /* * If the method has a return value, "result" will be an object or * a boxed primitive. */ result = dvmInvokeMethod(methObj, meth, argList, params, returnType, noAccessCheck); RETURN_PTR(result); init_failed: /* * If initialization failed, an exception will be raised. */ ALOGD("Method.invoke() on bad class %s failed", declaringClass->descriptor); assert(dvmCheckException(dvmThreadSelf())); RETURN_VOID(); }
/* * Issue a method call with arguments provided in an array. We process * the contents of "args" by scanning the method signature. * * The values were likely placed into an uninitialized jvalue array using * the field specifiers, which means that sub-32-bit fields (e.g. short, * boolean) may not have 32 or 64 bits of valid data. This is different * from the varargs invocation where the C compiler does a widening * conversion when calling a function. As a result, we have to be a * little more precise when pulling stuff out. * * "args" may be NULL if the method has no arguments. */ void dvmCallMethodA(Thread* self, const Method* method, Object* obj, bool fromJni, JValue* pResult, const jvalue* args) { const char* desc = &(method->shorty[1]); // [0] is the return type. int verifyCount = 0; ClassObject* clazz; u4* ins; #ifdef WITH_TAINT_TRACKING int slot_cnt = 0; bool nativeTarget = dvmIsNativeMethod(method); #endif clazz = callPrep(self, method, obj, false); if (clazz == NULL) return; /* "ins" for new frame start at frame pointer plus locals */ #ifdef WITH_TAINT_TRACKING if (nativeTarget) { /* native target, no taint tag interleaving */ ins = ((u4*)self->curFrame) + (method->registersSize - method->insSize); } else { /* interpreted target, taint tags are interleaved */ ins = ((u4*)self->curFrame) + ((method->registersSize - method->insSize) << 1); } #else ins = ((u4*)self->curFrame) + (method->registersSize - method->insSize); #endif /* put "this" pointer into in0 if appropriate */ if (!dvmIsStaticMethod(method)) { assert(obj != NULL); *ins++ = (u4) obj; /* obj is a "real" ref */ #ifdef WITH_TAINT_TRACKING if (!nativeTarget) { *ins++ = TAINT_CLEAR; } slot_cnt++; #endif verifyCount++; } JNIEnv* env = self->jniEnv; while (*desc != '\0') { switch (*desc++) { case 'D': /* 64-bit quantity; have to use */ case 'J': /* memcpy() in case of mis-alignment */ memcpy(ins, &args->j, 8); #ifdef WITH_TAINT_TRACKING if (nativeTarget) { ins += 2; } else { /* adjust for taint tag interleaving */ ins[2] = ins[1]; ins[1] = TAINT_CLEAR; ins[3] = TAINT_CLEAR; ins += 4; } slot_cnt += 2; #else ins += 2; #endif verifyCount++; /* this needs an extra push */ break; case 'L': /* includes array refs */ if (fromJni) *ins++ = (u4) dvmDecodeIndirectRef(env, args->l); else *ins++ = (u4) args->l; #ifdef WITH_TAINT_TRACKING if (!nativeTarget) { *ins++ = TAINT_CLEAR; } slot_cnt++; #endif break; case 'F': case 'I': *ins++ = args->i; /* full 32 bits */ #ifdef WITH_TAINT_TRACKING if (!nativeTarget) { *ins++ = TAINT_CLEAR; } slot_cnt++; #endif break; case 'S': *ins++ = args->s; /* 16 bits, sign-extended */ #ifdef WITH_TAINT_TRACKING if (!nativeTarget) { *ins++ = TAINT_CLEAR; } slot_cnt++; #endif break; case 'C': *ins++ = args->c; /* 16 bits, unsigned */ #ifdef WITH_TAINT_TRACKING if (!nativeTarget) { *ins++ = TAINT_CLEAR; } slot_cnt++; #endif break; case 'B': *ins++ = args->b; /* 8 bits, sign-extended */ #ifdef WITH_TAINT_TRACKING if (!nativeTarget) { *ins++ = TAINT_CLEAR; } slot_cnt++; #endif break; case 'Z': *ins++ = args->z; /* 8 bits, zero or non-zero */ #ifdef WITH_TAINT_TRACKING if (!nativeTarget) { *ins++ = TAINT_CLEAR; } slot_cnt++; #endif break; default: LOGE("Invalid char %c in short signature of %s.%s\n", *(desc-1), clazz->descriptor, method->name); assert(false); goto bail; } verifyCount++; args++; } #ifdef WITH_TAINT_TRACKING /* native hack spacer */ *ins++ = TAINT_CLEAR; /* if nativeTarget, this is return taint */ { int i; if (nativeTarget) { for (i = 0; i < slot_cnt; i++) { *ins++ = TAINT_CLEAR; } } } #endif #ifndef NDEBUG if (verifyCount != method->insSize) { LOGE("Got vfycount=%d insSize=%d for %s.%s\n", verifyCount, method->insSize, clazz->descriptor, method->name); assert(false); goto bail; } #endif if (dvmIsNativeMethod(method)) { TRACE_METHOD_ENTER(self, method); /* * Because we leave no space for local variables, "curFrame" points * directly at the method arguments. */ (*method->nativeFunc)(self->curFrame, pResult, method, self); TRACE_METHOD_EXIT(self, method); } else { #ifdef WITH_TAINT_TRACKING u4 rtaint; /* not used */ dvmInterpret(self, method, pResult, &rtaint); #else dvmInterpret(self, method, pResult); #endif } bail: dvmPopFrame(self); }
static void dalvik_dispatcher(const u4* args, jvalue* pResult, const Method* method, void* self) { ClassObject* returnType; jvalue result; ArrayObject* argArray; LOGD("dalvik_dispatcher source method: %s %s", method->name, method->shorty); Method* meth = (Method*) method->insns; meth->accessFlags = meth->accessFlags | ACC_PUBLIC; LOGD("dalvik_dispatcher target method: %s %s", method->name, method->shorty); returnType = dvmGetBoxedReturnType_fnPtr(method); if (returnType == NULL) { assert(dvmCheckException_fnPtr(self)); goto bail; } LOGD("dalvik_dispatcher start call->"); if (!dvmIsStaticMethod(meth)) { Object* thisObj = (Object*) args[0]; ClassObject* tmp = thisObj->clazz; thisObj->clazz = meth->clazz; argArray = boxMethodArgs(meth, args + 1); if (dvmCheckException_fnPtr(self)) goto bail; dvmCallMethod_fnPtr(self, (Method*) jInvokeMethod, dvmCreateReflectMethodObject_fnPtr(meth), &result, thisObj, argArray); thisObj->clazz = tmp; } else { argArray = boxMethodArgs(meth, args); if (dvmCheckException_fnPtr(self)) goto bail; dvmCallMethod_fnPtr(self, (Method*) jInvokeMethod, dvmCreateReflectMethodObject_fnPtr(meth), &result, NULL, argArray); } if (dvmCheckException_fnPtr(self)) { Object* excep = dvmGetException_fnPtr(self); jni_env->Throw((jthrowable) excep); goto bail; } if (returnType->primitiveType == PRIM_VOID) { LOGD("+++ ignoring return to void"); } else if (result.l == NULL) { if (dvmIsPrimitiveClass(returnType)) { jni_env->ThrowNew(NPEClazz, "null result when primitive expected"); goto bail; } pResult->l = NULL; } else { if (!dvmUnboxPrimitive_fnPtr(result.l, returnType, pResult)) { char msg[1024] = { 0 }; snprintf(msg, sizeof(msg) - 1, "%s!=%s\0", ((Object*) result.l)->clazz->descriptor, returnType->descriptor); jni_env->ThrowNew(CastEClazz, msg); goto bail; } } bail: dvmReleaseTrackedAlloc_fnPtr((Object*) argArray, self); }
/* * Invoke a method, using the specified arguments and return type, through * one of the reflection interfaces. Could be a virtual or direct method * (including constructors). Used for reflection. * * Deals with boxing/unboxing primitives and performs widening conversions. * * "invokeObj" will be null for a static method. * * If the invocation returns with an exception raised, we have to wrap it. */ Object* dvmInvokeMethod(Object* obj, const Method* method, ArrayObject* argList, ArrayObject* params, ClassObject* returnType, bool noAccessCheck) { ClassObject* clazz; Object* retObj = NULL; Thread* self = dvmThreadSelf(); s4* ins; int verifyCount, argListLength; JValue retval; bool needPop = false; #ifdef WITH_TAINT_TRACKING u4 rtaint = TAINT_CLEAR; int slot_cnt = 0; bool nativeTarget = dvmIsNativeMethod(method); /* For simplicity, argument tags for native targets * are unioned. This may cause false positives, but * it is the easiest way to handle this for now. */ u4 nativeTag = TAINT_CLEAR; #endif /* verify arg count */ if (argList != NULL) argListLength = argList->length; else argListLength = 0; if (argListLength != (int) params->length) { LOGI("invoke: expected %d args, received %d args\n", params->length, argListLength); dvmThrowException("Ljava/lang/IllegalArgumentException;", "wrong number of arguments"); return NULL; } clazz = callPrep(self, method, obj, !noAccessCheck); if (clazz == NULL) return NULL; needPop = true; /* "ins" for new frame start at frame pointer plus locals */ #ifdef WITH_TAINT_TRACKING if (nativeTarget) { /* native target, no taint tag interleaving */ ins = ((s4*)self->curFrame) + (method->registersSize - method->insSize); } else { /* interpreted target, taint tags are interleaved */ ins = ((s4*)self->curFrame) + ((method->registersSize - method->insSize) << 1); } #else ins = ((s4*)self->curFrame) + (method->registersSize - method->insSize); #endif verifyCount = 0; //LOGD(" FP is %p, INs live at >= %p\n", self->curFrame, ins); /* put "this" pointer into in0 if appropriate */ if (!dvmIsStaticMethod(method)) { assert(obj != NULL); *ins++ = (s4) obj; #ifdef WITH_TAINT_TRACKING if (!nativeTarget) { *ins++ = TAINT_CLEAR; } slot_cnt++; #endif verifyCount++; } /* * Copy the args onto the stack. Primitive types are converted when * necessary, and object types are verified. */ DataObject** args; ClassObject** types; int i; args = (DataObject**) argList->contents; types = (ClassObject**) params->contents; for (i = 0; i < argListLength; i++) { int width; #ifdef WITH_TAINT_TRACKING int tag = dvmGetPrimitiveTaint(*args, *types); #endif width = dvmConvertArgument(*args++, *types++, ins); if (width < 0) { if (*(args-1) != NULL) { LOGV("invoke: type mismatch on arg %d ('%s' '%s')\n", i, (*(args-1))->obj.clazz->descriptor, (*(types-1))->descriptor); } dvmPopFrame(self); // throw wants to pull PC out of stack needPop = false; dvmThrowException("Ljava/lang/IllegalArgumentException;", "argument type mismatch"); goto bail; } #ifdef WITH_TAINT_TRACKING /* dvmConvertArgument() returns -1, 1, or 2 */ if (nativeTarget) { nativeTag |= tag; /* TODO: is there a better way to do this?*/ ins += width; } else { if (width == 2) { ins[2] = ins[1]; ins[1] = tag; ins[3] = tag; ins += 4; } else if (width == 1) { ins[1] = tag; ins += 2; } else { /* error condition duplicated from above */ dvmPopFrame(self); dvmThrowException("Ljava/lang/IllegalArgumentException;", "argument type mismatch"); goto bail_popped; } } slot_cnt += width; #else ins += width; #endif verifyCount += width; } #ifdef WITH_TAINT_TRACKING /* native hack spacer */ *ins++ = TAINT_CLEAR; /* if nativeTarget, this is return taint */ { int i; if (nativeTarget) { for (i = 0; i < slot_cnt; i++) { *ins++ = nativeTag; /* TODO: better way? */ } } } #endif if (verifyCount != method->insSize) { LOGE("Got vfycount=%d insSize=%d for %s.%s\n", verifyCount, method->insSize, clazz->descriptor, method->name); assert(false); goto bail; } //dvmDumpThreadStack(dvmThreadSelf()); if (dvmIsNativeMethod(method)) { TRACE_METHOD_ENTER(self, method); /* * Because we leave no space for local variables, "curFrame" points * directly at the method arguments. */ (*method->nativeFunc)(self->curFrame, &retval, method, self); TRACE_METHOD_EXIT(self, method); #ifdef WITH_TAINT_TRACKING rtaint = ((u4*)self->curFrame)[slot_cnt]; #endif } else { #ifdef WITH_TAINT_TRACKING dvmInterpret(self, method, &retval, &rtaint); #else dvmInterpret(self, method, &retval); #endif } /* * Pop the frame immediately. The "wrap" calls below can cause * allocations, and we don't want the GC to walk the now-dead frame. */ dvmPopFrame(self); needPop = false; /* * If an exception is raised, wrap and replace. This is necessary * because the invoked method could have thrown a checked exception * that the caller wasn't prepared for. * * We might be able to do this up in the interpreted code, but that will * leave us with a shortened stack trace in the top-level exception. */ if (dvmCheckException(self)) { dvmWrapException("Ljava/lang/reflect/InvocationTargetException;"); } else { /* * If this isn't a void method or constructor, convert the return type * to an appropriate object. * * We don't do this when an exception is raised because the value * in "retval" is undefined. */ if (returnType != NULL) { retObj = (Object*)dvmWrapPrimitive(retval, returnType); #ifdef WITH_TAINT_TRACKING dvmSetPrimitiveTaint((DataObject *)retObj, returnType, rtaint); #endif dvmReleaseTrackedAlloc(retObj, NULL); } } bail: if (needPop) { dvmPopFrame(self); } bail_popped: return retObj; }
/* * Invoke a method, using the specified arguments and return type, through * one of the reflection interfaces. Could be a virtual or direct method * (including constructors). Used for reflection. * * Deals with boxing/unboxing primitives and performs widening conversions. * * "invokeObj" will be null for a static method. * * If the invocation returns with an exception raised, we have to wrap it. */ Object* dvmInvokeMethod(Object* obj, const Method* method, ArrayObject* argList, ArrayObject* params, ClassObject* returnType, bool noAccessCheck) { ClassObject* clazz; Object* retObj = NULL; Thread* self = dvmThreadSelf(); s4* ins; int verifyCount, argListLength; JValue retval; /* verify arg count */ if (argList != NULL) argListLength = argList->length; else argListLength = 0; if (argListLength != (int) params->length) { LOGI("invoke: expected %d args, received %d args\n", params->length, argListLength); dvmThrowException("Ljava/lang/IllegalArgumentException;", "wrong number of arguments"); return NULL; } clazz = callPrep(self, method, obj, !noAccessCheck); if (clazz == NULL) return NULL; /* "ins" for new frame start at frame pointer plus locals */ ins = ((s4*)self->curFrame) + (method->registersSize - method->insSize); verifyCount = 0; //LOGD(" FP is %p, INs live at >= %p\n", self->curFrame, ins); /* put "this" pointer into in0 if appropriate */ if (!dvmIsStaticMethod(method)) { assert(obj != NULL); *ins++ = (s4) obj; verifyCount++; } /* * Copy the args onto the stack. Primitive types are converted when * necessary, and object types are verified. */ DataObject** args; ClassObject** types; int i; args = (DataObject**) argList->contents; types = (ClassObject**) params->contents; for (i = 0; i < argListLength; i++) { int width; width = dvmConvertArgument(*args++, *types++, ins); if (width < 0) { if (*(args-1) != NULL) { LOGV("invoke: type mismatch on arg %d ('%s' '%s')\n", i, (*(args-1))->obj.clazz->descriptor, (*(types-1))->descriptor); } dvmPopFrame(self); // throw wants to pull PC out of stack dvmThrowException("Ljava/lang/IllegalArgumentException;", "argument type mismatch"); goto bail_popped; } ins += width; verifyCount += width; } if (verifyCount != method->insSize) { LOGE("Got vfycount=%d insSize=%d for %s.%s\n", verifyCount, method->insSize, clazz->descriptor, method->name); assert(false); goto bail; } //dvmDumpThreadStack(dvmThreadSelf()); if (dvmIsNativeMethod(method)) { #ifdef WITH_PROFILER TRACE_METHOD_ENTER(self, method); #endif /* * Because we leave no space for local variables, "curFrame" points * directly at the method arguments. */ (*method->nativeFunc)(self->curFrame, &retval, method, self); #ifdef WITH_PROFILER TRACE_METHOD_EXIT(self, method); #endif } else { dvmInterpret(self, method, &retval); } /* * If an exception is raised, wrap and replace. This is necessary * because the invoked method could have thrown a checked exception * that the caller wasn't prepared for. * * We might be able to do this up in the interpreted code, but that will * leave us with a shortened stack trace in the top-level exception. */ if (dvmCheckException(self)) { dvmWrapException("Ljava/lang/reflect/InvocationTargetException;"); } else { /* * If this isn't a void method or constructor, convert the return type * to an appropriate object. * * We don't do this when an exception is raised because the value * in "retval" is undefined. */ if (returnType != NULL) { retObj = (Object*)dvmWrapPrimitive(retval, returnType); dvmReleaseTrackedAlloc(retObj, NULL); } } bail: dvmPopFrame(self); bail_popped: return retObj; }
/* * Invoke a method, using the specified arguments and return type, through * one of the reflection interfaces. Could be a virtual or direct method * (including constructors). Used for reflection. * * Deals with boxing/unboxing primitives and performs widening conversions. * * "invokeObj" will be null for a static method. * * If the invocation returns with an exception raised, we have to wrap it. */ Object* dvmInvokeMethod(Object* obj, const Method* method, ArrayObject* argList, ArrayObject* params, ClassObject* returnType, bool noAccessCheck) { //salma // __android_log_print(ANDROID_LOG_DEBUG, "DVM DEBUG", "dvmInvokeMethod method name = %s\n,clazz name: %s", method->name, method->clazz->descriptor); //end salma ClassObject* clazz; Object* retObj = NULL; Thread* self = dvmThreadSelf(); s4* ins; int verifyCount, argListLength; JValue retval; bool needPop = false; /* verify arg count */ if (argList != NULL) argListLength = argList->length; else argListLength = 0; if (argListLength != (int) params->length) { dvmThrowExceptionFmt(gDvm.exIllegalArgumentException, "wrong number of arguments; expected %d, got %d", params->length, argListLength); return NULL; } clazz = callPrep(self, method, obj, !noAccessCheck); if (clazz == NULL) return NULL; needPop = true; /* "ins" for new frame start at frame pointer plus locals */ ins = ((s4*)self->interpSave.curFrame) + (method->registersSize - method->insSize); verifyCount = 0; //ALOGD(" FP is %p, INs live at >= %p", self->interpSave.curFrame, ins); /* put "this" pointer into in0 if appropriate */ if (!dvmIsStaticMethod(method)) { assert(obj != NULL); *ins++ = (s4) obj; verifyCount++; } /* * Copy the args onto the stack. Primitive types are converted when * necessary, and object types are verified. */ DataObject** args = (DataObject**)(void*)argList->contents; ClassObject** types = (ClassObject**)(void*)params->contents; for (int i = 0; i < argListLength; i++) { int width = dvmConvertArgument(*args++, *types++, ins); if (width < 0) { dvmPopFrame(self); // throw wants to pull PC out of stack needPop = false; throwArgumentTypeMismatch(i, *(types-1), *(args-1)); goto bail; } ins += width; verifyCount += width; } #ifndef NDEBUG if (verifyCount != method->insSize) { ALOGE("Got vfycount=%d insSize=%d for %s.%s", verifyCount, method->insSize, clazz->descriptor, method->name); assert(false); goto bail; } #endif if (dvmIsNativeMethod(method)) { TRACE_METHOD_ENTER(self, method); /* * Because we leave no space for local variables, "curFrame" points * directly at the method arguments. */ (*method->nativeFunc)((u4*)self->interpSave.curFrame, &retval, method, self); TRACE_METHOD_EXIT(self, method); } else { dvmInterpret(self, method, &retval); } /* * Pop the frame immediately. The "wrap" calls below can cause * allocations, and we don't want the GC to walk the now-dead frame. */ dvmPopFrame(self); needPop = false; /* * If an exception is raised, wrap and replace. This is necessary * because the invoked method could have thrown a checked exception * that the caller wasn't prepared for. * * We might be able to do this up in the interpreted code, but that will * leave us with a shortened stack trace in the top-level exception. */ if (dvmCheckException(self)) { dvmWrapException("Ljava/lang/reflect/InvocationTargetException;"); } else { /* * If this isn't a void method or constructor, convert the return type * to an appropriate object. * * We don't do this when an exception is raised because the value * in "retval" is undefined. */ if (returnType != NULL) { retObj = (Object*)dvmBoxPrimitive(retval, returnType); dvmReleaseTrackedAlloc(retObj, NULL); } } bail: if (needPop) { dvmPopFrame(self); } return retObj; }
/* * Issue a method call with a variable number of arguments. We process * the contents of "args" by scanning the method signature. * * Pass in NULL for "obj" on calls to static methods. * * We don't need to take the class as an argument because, in Dalvik, * we don't need to worry about static synchronized methods. */ void dvmCallMethodV(Thread* self, const Method* method, Object* obj, bool fromJni, JValue* pResult, va_list args) { const char* desc = &(method->shorty[1]); // [0] is the return type. int verifyCount = 0; ClassObject* clazz; u4* ins; #ifdef WITH_TAINT_TRACKING int slot_cnt = 0; bool nativeTarget = dvmIsNativeMethod(method); #endif clazz = callPrep(self, method, obj, false); if (clazz == NULL) return; /* "ins" for new frame start at frame pointer plus locals */ #ifdef WITH_TAINT_TRACKING if (nativeTarget) { /* native target, no taint tag interleaving */ ins = ((u4*)self->curFrame) + (method->registersSize - method->insSize); } else { /* interpreted target, taint tags are interleaved */ ins = ((u4*)self->curFrame) + ((method->registersSize - method->insSize) << 1); } #else ins = ((u4*)self->curFrame) + (method->registersSize - method->insSize); #endif //LOGD(" FP is %p, INs live at >= %p\n", self->curFrame, ins); /* put "this" pointer into in0 if appropriate */ if (!dvmIsStaticMethod(method)) { #ifdef WITH_EXTRA_OBJECT_VALIDATION assert(obj != NULL && dvmIsValidObject(obj)); #endif *ins++ = (u4) obj; #ifdef WITH_TAINT_TRACKING if (!nativeTarget) { *ins++ = TAINT_CLEAR; } slot_cnt++; #endif verifyCount++; } JNIEnv* env = self->jniEnv; while (*desc != '\0') { switch (*(desc++)) { case 'D': case 'J': { u8 val = va_arg(args, u8); memcpy(ins, &val, 8); // EABI prevents direct store #ifdef WITH_TAINT_TRACKING if (nativeTarget) { ins += 2; } else { /* adjust for taint tag interleaving */ ins[2] = ins[1]; ins[1] = TAINT_CLEAR; ins[3] = TAINT_CLEAR; ins += 4; } slot_cnt += 2; #else ins += 2; #endif verifyCount += 2; break; } case 'F': { /* floats were normalized to doubles; convert back */ float f = (float) va_arg(args, double); *ins++ = dvmFloatToU4(f); #ifdef WITH_TAINT_TRACKING if (!nativeTarget) { *ins++ = TAINT_CLEAR; } slot_cnt++; #endif verifyCount++; break; } case 'L': { /* 'shorty' descr uses L for all refs, incl array */ void* argObj = va_arg(args, void*); assert(obj == NULL || dvmIsValidObject(obj)); if (fromJni) *ins++ = (u4) dvmDecodeIndirectRef(env, argObj); else *ins++ = (u4) argObj; #ifdef WITH_TAINT_TRACKING if (!nativeTarget) { *ins++ = TAINT_CLEAR; } slot_cnt++; #endif verifyCount++; break; } default: { /* Z B C S I -- all passed as 32-bit integers */ *ins++ = va_arg(args, u4); #ifdef WITH_TAINT_TRACKING if (!nativeTarget) { *ins++ = TAINT_CLEAR; } slot_cnt++; #endif verifyCount++; break; } } } #ifdef WITH_TAINT_TRACKING /* native hack spacer */ *ins++ = TAINT_CLEAR; /* if nativeTarget, this is return taint */ { int i; if (nativeTarget) { for (i = 0; i < slot_cnt; i++) { *ins++ = TAINT_CLEAR; } } } #endif #ifndef NDEBUG if (verifyCount != method->insSize) { LOGE("Got vfycount=%d insSize=%d for %s.%s\n", verifyCount, method->insSize, clazz->descriptor, method->name); assert(false); goto bail; } #endif //dvmDumpThreadStack(dvmThreadSelf()); if (dvmIsNativeMethod(method)) { TRACE_METHOD_ENTER(self, method); /* * Because we leave no space for local variables, "curFrame" points * directly at the method arguments. */ (*method->nativeFunc)(self->curFrame, pResult, method, self); TRACE_METHOD_EXIT(self, method); } else { #ifdef WITH_TAINT_TRACKING u4 rtaint; /* not used */ dvmInterpret(self, method, pResult, &rtaint); #else dvmInterpret(self, method, pResult); #endif } #ifndef NDEBUG bail: #endif dvmPopFrame(self); }
static void dexspyCallHandler(const u4* args, JValue* pResult, const Method* method, ::Thread* self) { OriginalMethodsIt original = findOriginalMethod(method); if (original == dexspyOriginalMethods.end()) { dvmThrowNoSuchMethodError("could not find Dexspy original method - how did you even get here?"); return; } ThreadStatus oldThreadStatus = self->status; JNIEnv* env = self->jniEnv; // get java.lang.reflect.Method object for original method jobject originalReflected = env->ToReflectedMethod( (jclass)dexspyAddLocalReference(self, original->clazz), (jmethodID)method, true); // convert/box arguments const char* desc = &method->shorty[1]; // [0] is the return type. Object* thisObject = NULL; size_t srcIndex = 0; size_t dstIndex = 0; // for non-static methods determine the "this" pointer if (!dvmIsStaticMethod(&(*original))) { thisObject = (Object*) dexspyAddLocalReference(self, (Object*)args[0]); srcIndex++; } jclass objectClass = env->FindClass("java/lang/Object"); jobjectArray argsArray = env->NewObjectArray(strlen(method->shorty) - 1, objectClass, NULL); while (*desc != '\0') { char descChar = *(desc++); JValue value; Object* obj; switch (descChar) { case 'Z': case 'C': case 'F': case 'B': case 'S': case 'I': value.i = args[srcIndex++]; obj = (Object*) dvmBoxPrimitive(value, dvmFindPrimitiveClass(descChar)); dvmReleaseTrackedAlloc(obj, NULL); break; case 'D': case 'J': value.j = dvmGetArgLong(args, srcIndex); srcIndex += 2; obj = (Object*) dvmBoxPrimitive(value, dvmFindPrimitiveClass(descChar)); dvmReleaseTrackedAlloc(obj, NULL); break; case '[': case 'L': obj = (Object*) args[srcIndex++]; break; default: ALOGE("Unknown method signature description character: %c\n", descChar); obj = NULL; srcIndex++; } env->SetObjectArrayElement(argsArray, dstIndex++, dexspyAddLocalReference(self, obj)); } // call the Java handler function jobject resultRef = env->CallStaticObjectMethod( dexspyClass, dexspyHandleHookedMethod, originalReflected, thisObject, argsArray); // exceptions are thrown to the caller if (env->ExceptionCheck()) { dvmChangeStatus(self, oldThreadStatus); return; } // return result with proper type Object* result = dvmDecodeIndirectRef(self, resultRef); ClassObject* returnType = dvmGetBoxedReturnType(method); if (returnType->primitiveType == PRIM_VOID) { // ignored } else if (result == NULL) { if (dvmIsPrimitiveClass(returnType)) { dvmThrowNullPointerException("null result when primitive expected"); } pResult->l = NULL; } else { if (!dvmUnboxPrimitive(result, returnType, pResult)) { dvmThrowClassCastException(result->clazz, returnType); } } // set the thread status back to running. must be done after the last env->...() dvmChangeStatus(self, oldThreadStatus); }
static void xposedCallHandler(const u4* args, JValue* pResult, const Method* method, ::Thread* self) { if (!xposedIsHooked(method)) { dvmThrowNoSuchMethodError( "could not find Xposed original method - how did you even get here?"); return; } XposedHookInfo* hookInfo = (XposedHookInfo*) method->insns; Method* original = (Method*) hookInfo; Object* originalReflected = hookInfo->reflectedMethod; Object* additionalInfo = hookInfo->additionalInfo; // convert/box arguments const char* desc = &method->shorty[1]; // [0] is the return type. Object* thisObject = NULL; size_t srcIndex = 0; size_t dstIndex = 0; // for non-static methods determine the "this" pointer if (!dvmIsStaticMethod(original)) { thisObject = (Object*) args[0]; srcIndex++; } ArrayObject* argsArray = dvmAllocArrayByClass(objectArrayClass, strlen(method->shorty) - 1, ALLOC_DEFAULT); if (argsArray == NULL) { return; } while (*desc != '\0') { char descChar = *(desc++); JValue value; Object* obj; switch (descChar) { case 'Z': case 'C': case 'F': case 'B': case 'S': case 'I': value.i = args[srcIndex++]; obj = (Object*) dvmBoxPrimitive(value, dvmFindPrimitiveClass(descChar)); dvmReleaseTrackedAlloc(obj, self); break; case 'D': case 'J': value.j = dvmGetArgLong(args, srcIndex); srcIndex += 2; obj = (Object*) dvmBoxPrimitive(value, dvmFindPrimitiveClass(descChar)); dvmReleaseTrackedAlloc(obj, self); break; case '[': case 'L': obj = (Object*) args[srcIndex++]; break; default: ALOGE("Unknown method signature description character: %c\n", descChar); obj = NULL; srcIndex++; } xposedSetObjectArrayElement(argsArray, dstIndex++, obj); } // call the Java handler function JValue result; dvmCallMethod(self, xposedHandleHookedMethod, NULL, &result, originalReflected, (int) original, additionalInfo, thisObject, argsArray); dvmReleaseTrackedAlloc(argsArray, self); // exceptions are thrown to the caller if (dvmCheckException(self)) { return; } // return result with proper type ClassObject* returnType = dvmGetBoxedReturnType(method); if (returnType->primitiveType == PRIM_VOID) { // ignored } else if (result.l == NULL) { if (dvmIsPrimitiveClass(returnType)) { dvmThrowNullPointerException("null result when primitive expected"); } pResult->l = NULL; } else { if (!dvmUnboxPrimitive(result.l, returnType, pResult)) { dvmThrowClassCastException(result.l->clazz, returnType); } } }