/*
 * Find the return type in the signature, and convert it to a class
 * object.  For primitive types we use a boxed class, for reference types
 * we do a name lookup.
 *
 * On failure, we return NULL with an exception raised.
 */
ClassObject* dvmGetBoxedReturnType(const Method* meth)
{
    const char* sig = dexProtoGetReturnType(&meth->prototype);

    switch (*sig) {
    case 'Z':
    case 'C':
    case 'F':
    case 'D':
    case 'B':
    case 'S':
    case 'I':
    case 'J':
    case 'V':
        return dvmFindPrimitiveClass(*sig);
    case '[':
    case 'L':
        return dvmFindClass(sig, meth->clazz->classLoader);
    default: {
        /* should not have passed verification */
        char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
        ALOGE("Bad return type in signature '%s'", desc);
        free(desc);
        dvmThrowInternalError(NULL);
        return NULL;
    }
    }
}
/*
 * static final Class getPrimitiveClass(char prim_type)
 */
static void Dalvik_java_lang_VMClassLoader_getPrimitiveClass(const u4* args,
    JValue* pResult)
{
    int primType = args[0];

    pResult->l = dvmFindPrimitiveClass(primType);
}
/*
 * Find the named class object.  We have to trim "*pSignature" down to just
 * the first token, do the lookup, and then restore anything important
 * that we've stomped on.
 *
 * "pSig" will be advanced to the start of the next token.
 */
static ClassObject* convertSignaturePartToClass(char** pSignature,
    const ClassObject* defClass)
{
    ClassObject* clazz = NULL;
    char* signature = *pSignature;

    if (*signature == '[') {
        /* looks like "[[[Landroid/debug/Stuff;"; we want the whole thing */
        char savedChar;

        while (*++signature == '[')
            ;
        if (*signature == 'L') {
            while (*++signature != ';')
                ;
        }

        /* advance past ';', and stomp on whatever comes next */
        savedChar = *++signature;
        *signature = '\0';
        clazz = dvmFindArrayClass(*pSignature, defClass->classLoader);
        *signature = savedChar;
    } else if (*signature == 'L') {
        /* looks like 'Landroid/debug/Stuff;"; we want the whole thing */
        char savedChar;
        while (*++signature != ';')
            ;
        savedChar = *++signature;
        *signature = '\0';
        clazz = dvmFindClassNoInit(*pSignature, defClass->classLoader);
        *signature = savedChar;
    } else {
        clazz = dvmFindPrimitiveClass(*signature++);
    }

    if (clazz == NULL) {
        ALOGW("Unable to match class for part: '%s'", *pSignature);
    }
    *pSignature = signature;
    return clazz;
}
// Based on dvmResolveClass.
static void preloadDexCachesResolveType(DvmDex* pDvmDex, uint32_t typeIdx) {
    ClassObject* clazz = dvmDexGetResolvedClass(pDvmDex, typeIdx);
    if (clazz != NULL) {
        return;
    }
    const DexFile* pDexFile = pDvmDex->pDexFile;
    const char* className = dexStringByTypeIdx(pDexFile, typeIdx);
    if (className[0] != '\0' && className[1] == '\0') {
        /* primitive type */
        clazz = dvmFindPrimitiveClass(className[0]);
    } else {
        clazz = dvmLookupClass(className, NULL, true);
    }
    if (clazz == NULL) {
        return;
    }
    // Skip uninitialized classes because filled cache entry implies it is initialized.
    if (!dvmIsClassInitialized(clazz)) {
        // ALOGI("VMRuntime.preloadDexCaches uninitialized clazz=%s", className);
        return;
    }
    // ALOGI("VMRuntime.preloadDexCaches found clazz=%s", className);
    dvmDexSetResolvedClass(pDvmDex, typeIdx, clazz);
}
Exemple #5
0
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);
}
Exemple #6
0
/*
 * Return a new Object[] array with the contents of "args".  We determine
 * the number and types of values in "args" based on the method signature.
 * Primitive types are boxed.
 *
 * Returns NULL if the method takes no arguments.
 *
 * The caller must call dvmReleaseTrackedAlloc() on the return value.
 *
 * On failure, returns with an appropriate exception raised.
 */
static ArrayObject* boxMethodArgs(const Method* method, const u4* args)
{
    const char* desc = &method->shorty[1]; // [0] is the return type.
    ArrayObject* argArray = NULL;
    int argCount;
    Object** argObjects;
    bool failed = true;

    /* count args */
    argCount = dexProtoGetParameterCount(&method->prototype);

    /* allocate storage */
    argArray = dvmAllocArray(gDvm.classJavaLangObjectArray, argCount,
        kObjectArrayRefWidth, ALLOC_DEFAULT);
    if (argArray == NULL)
        goto bail;
    argObjects = (Object**) argArray->contents;

    /*
     * Fill in the array.
     */

    int srcIndex = 0;
    
    argCount = 0;
    while (*desc != '\0') {
        char descChar = *(desc++);
        JValue value;

        switch (descChar) {
        case 'Z':
        case 'C':
        case 'F':
        case 'B':
        case 'S':
        case 'I':
            value.i = args[srcIndex++];
            argObjects[argCount] = (Object*) dvmWrapPrimitive(value,
                dvmFindPrimitiveClass(descChar));
            /* argObjects is tracked, don't need to hold this too */
            dvmReleaseTrackedAlloc(argObjects[argCount], NULL);
            argCount++;
            break;
        case 'D':
        case 'J':
            value.j = dvmGetArgLong(args, srcIndex);
            srcIndex += 2;
            argObjects[argCount] = (Object*) dvmWrapPrimitive(value,
                dvmFindPrimitiveClass(descChar));
            dvmReleaseTrackedAlloc(argObjects[argCount], NULL);
            argCount++;
            break;
        case '[':
        case 'L':
            argObjects[argCount++] = (Object*) args[srcIndex++];
            break;
        }
    }

    failed = false;

bail:
    if (failed) {
        dvmReleaseTrackedAlloc((Object*)argArray, NULL);
        argArray = NULL;
    }
    return argArray;
}
/*
 * Find the class corresponding to "classIdx", which maps to a class name
 * string.  It might be in the same DEX file as "referrer", in a different
 * DEX file, generated by a class loader, or generated by the VM (e.g.
 * array classes).
 *
 * Because the DexTypeId is associated with the referring class' DEX file,
 * we may have to resolve the same class more than once if it's referred
 * to from classes in multiple DEX files.  This is a necessary property for
 * DEX files associated with different class loaders.
 *
 * We cache a copy of the lookup in the DexFile's "resolved class" table,
 * so future references to "classIdx" are faster.
 *
 * Note that "referrer" may be in the process of being linked.
 *
 * Traditional VMs might do access checks here, but in Dalvik the class
 * "constant pool" is shared between all classes in the DEX file.  We rely
 * on the verifier to do the checks for us.
 *
 * Does not initialize the class.
 *
 * "fromUnverifiedConstant" should only be set if this call is the direct
 * result of executing a "const-class" or "instance-of" instruction, which
 * use class constants not resolved by the bytecode verifier.
 *
 * Returns NULL with an exception raised on failure.
 */
ClassObject* dvmResolveClass(const ClassObject* referrer, u4 classIdx,
                             bool fromUnverifiedConstant)
{
    DvmDex* pDvmDex = referrer->pDvmDex;
    ClassObject* resClass;
    const char* className;

    /*
     * Check the table first -- this gets called from the other "resolve"
     * methods.
     */
    resClass = dvmDexGetResolvedClass(pDvmDex, classIdx);
    if (resClass != NULL)
        return resClass;

    LOGVV("--- resolving class %u (referrer=%s cl=%p)\n",
          classIdx, referrer->descriptor, referrer->classLoader);

    /*
     * Class hasn't been loaded yet, or is in the process of being loaded
     * and initialized now.  Try to get a copy.  If we find one, put the
     * pointer in the DexTypeId.  There isn't a race condition here --
     * 32-bit writes are guaranteed atomic on all target platforms.  Worst
     * case we have two threads storing the same value.
     *
     * If this is an array class, we'll generate it here.
     */
    className = dexStringByTypeIdx(pDvmDex->pDexFile, classIdx);
    if (className[0] != '\0' && className[1] == '\0') {
        /* primitive type */
        resClass = dvmFindPrimitiveClass(className[0]);
    } else {
        resClass = dvmFindClassNoInit(className, referrer->classLoader);
    }

    if (resClass != NULL) {
        /*
         * If the referrer was pre-verified, the resolved class must come
         * from the same DEX or from a bootstrap class.  The pre-verifier
         * makes assumptions that could be invalidated by a wacky class
         * loader.  (See the notes at the top of oo/Class.c.)
         *
         * The verifier does *not* fail a class for using a const-class
         * or instance-of instruction referring to an unresolveable class,
         * because the result of the instruction is simply a Class object
         * or boolean -- there's no need to resolve the class object during
         * verification.  Instance field and virtual method accesses can
         * break dangerously if we get the wrong class, but const-class and
         * instance-of are only interesting at execution time.  So, if we
         * we got here as part of executing one of the "unverified class"
         * instructions, we skip the additional check.
         *
         * Ditto for class references from annotations and exception
         * handler lists.
         */
        if (!fromUnverifiedConstant &&
                IS_CLASS_FLAG_SET(referrer, CLASS_ISPREVERIFIED))
        {
            ClassObject* resClassCheck = resClass;
            if (dvmIsArrayClass(resClassCheck))
                resClassCheck = resClassCheck->elementClass;

            if (referrer->pDvmDex != resClassCheck->pDvmDex &&
                    resClassCheck->classLoader != NULL)
            {
                LOGW("Class resolved by unexpected DEX:"
                     " %s(%p):%p ref [%s] %s(%p):%p\n",
                     referrer->descriptor, referrer->classLoader,
                     referrer->pDvmDex,
                     resClass->descriptor, resClassCheck->descriptor,
                     resClassCheck->classLoader, resClassCheck->pDvmDex);
                LOGW("(%s had used a different %s during pre-verification)\n",
                     referrer->descriptor, resClass->descriptor);
                dvmThrowException("Ljava/lang/IllegalAccessError;",
                                  "Class ref in pre-verified class resolved to unexpected "
                                  "implementation");
                return NULL;
            }
        }

        LOGVV("##### +ResolveClass(%s): referrer=%s dex=%p ldr=%p ref=%d\n",
              resClass->descriptor, referrer->descriptor, referrer->pDvmDex,
              referrer->classLoader, classIdx);

        /*
         * Add what we found to the list so we can skip the class search
         * next time through.
         *
         * TODO: should we be doing this when fromUnverifiedConstant==true?
         * (see comments at top of oo/Class.c)
         */
        dvmDexSetResolvedClass(pDvmDex, classIdx, resClass);
    } else {
        /* not found, exception should be raised */
        LOGVV("Class not found: %s\n",
              dexStringByTypeIdx(pDvmDex->pDexFile, classIdx));
        assert(dvmCheckException(dvmThreadSelf()));
    }

    return resClass;
}
/*
 * Alternate version of dvmResolveClass for use with verification and
 * optimization.  Performs access checks on every resolve, and refuses
 * to acknowledge the existence of classes defined in more than one DEX
 * file.
 *
 * Exceptions caused by failures are cleared before returning.
 *
 * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
 */
ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx,
    VerifyError* pFailure)
{
    DvmDex* pDvmDex = referrer->pDvmDex;
    ClassObject* resClass;

    /*
     * Check the table first.  If not there, do the lookup by name.
     */
    resClass = dvmDexGetResolvedClass(pDvmDex, classIdx);
    if (resClass == NULL) {
        const char* className = dexStringByTypeIdx(pDvmDex->pDexFile, classIdx);
        if (className[0] != '\0' && className[1] == '\0') {
            /* primitive type */
            resClass = dvmFindPrimitiveClass(className[0]);
        } else {
            resClass = dvmFindClassNoInit(className, referrer->classLoader);
        }
        if (resClass == NULL) {
            /* not found, exception should be raised */
            ALOGV("DexOpt: class %d (%s) not found",
                classIdx,
                dexStringByTypeIdx(pDvmDex->pDexFile, classIdx));
            if (pFailure != NULL) {
                /* dig through the wrappers to find the original failure */
                Object* excep = dvmGetException(dvmThreadSelf());
                while (true) {
                    Object* cause = dvmGetExceptionCause(excep);
                    if (cause == NULL)
                        break;
                    excep = cause;
                }
                if (strcmp(excep->clazz->descriptor,
                    "Ljava/lang/IncompatibleClassChangeError;") == 0)
                {
                    *pFailure = VERIFY_ERROR_CLASS_CHANGE;
                } else {
                    *pFailure = VERIFY_ERROR_NO_CLASS;
                }
            }
            dvmClearOptException(dvmThreadSelf());
            return NULL;
        }

        /*
         * Add it to the resolved table so we're faster on the next lookup.
         */
        dvmDexSetResolvedClass(pDvmDex, classIdx, resClass);
    }

    /* multiple definitions? */
    if (IS_CLASS_FLAG_SET(resClass, CLASS_MULTIPLE_DEFS)) {
        ALOGI("DexOpt: not resolving ambiguous class '%s'",
            resClass->descriptor);
        if (pFailure != NULL)
            *pFailure = VERIFY_ERROR_NO_CLASS;
        return NULL;
    }

    /* access allowed? */
    tweakLoader(referrer, resClass);
    bool allowed = dvmCheckClassAccess(referrer, resClass);
    untweakLoader(referrer, resClass);
    if (!allowed) {
        ALOGW("DexOpt: resolve class illegal access: %s -> %s",
            referrer->descriptor, resClass->descriptor);
        if (pFailure != NULL)
            *pFailure = VERIFY_ERROR_ACCESS_CLASS;
        return NULL;
    }

    return resClass;
}
Exemple #9
0
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);
		}
	}
}