Valerio `sid` Costamagna bio photo

Valerio `sid` Costamagna

“It’s no use going back to yesterday, because I was a different person then.” ― Lewis Carroll, Alice in Wonderland

Email Twitter Github

Hooking virtual methods for fun and profit

What do you need for hooking Java virtual methods? Suppose you want to hook the method A within class Z and divert the control flow to the new method B ( the “patch” method ) within class X. At least you have to:

  1. load X into application’s memory
  2. retrive A reference from memory and its position inside the Z vtable
  3. modify the A entry within Z vtable with B address

Using the GetMethodID function of Java Native Interface (JNI) we can obtain the A memory reference (suppose A is already loaded in memory,true for Android APIs). The GetMethodID function returns an jobject type, which is just an alias to ArtMethod data structure.

Using relative offset, we can access to elements inside the ArtMethod object returned by GetMethodID for retriving the information needed to retrive A index value inside Z vtable. Relative offset approach is stable and reliable because ART internals are very unlikely to be subject of OEM modifications.

To achieve point 1 we use the DexClassLoader API, the “patch” method B is defined by the user and loaded from a DEX file.

Once we have got the information scanning the memory, we can achieve point 3 using just simple memory operations by native code.

Using the A memory reference returned by GetMethodID , we can parse the ArtMethod structure and access to its following elements:

  1. declaring_class_
  2. method_index_

The former is a reference to Z (a Class object). This class contains the method A. The latter is the A’s index value inside Z vtable.

Following the declaring_class_ pointer, we can parse the Class object from memory to access its following elements:

  • vtable_
  • virtual_methods_

How does the runtime is looking up virtual methods?

First let’s try to follow the flow of a Java method called by JNI. As example we take in account the CallObjectMethod function, defined in jniinternals.c. This function is used for calling Java methods from native code, exactly only methods which returns an Object type.

(All following source code is taken from androidxref Android 6.0.1_r10 )

680  static jobject CallObjectMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
681    va_list ap;
682    va_start(ap, mid);
683    CHECK_NON_NULL_ARGUMENT(obj);
684    CHECK_NON_NULL_ARGUMENT(mid);
685    ScopedObjectAccess soa(env);
686    JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap));
687    va_end(ap);
688    return soa.AddLocalReference<jobject>(result.GetL());
689  }

The called function InvokeVirtualOrInterfaceWithVarArgs (line 686) is defined in reflection.cc:

529JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,
530                                           jobject obj, jmethodID mid, va_list args) {
	 [...]
539  mirror::Object* receiver = soa.Decode<mirror::Object*>(obj);
540  ArtMethod* method = FindVirtualMethod(receiver, soa.DecodeMethod(mid));
541  bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
	 [...]
547  uint32_t shorty_len = 0;
548  const char* shorty = method->GetInterfaceMethodIfProxy(sizeof(void*))->GetShorty(&shorty_len);
549  JValue result;
550  ArgArray arg_array(shorty, shorty_len);
551  arg_array.BuildArgArrayFromVarArgs(soa, receiver, args);
552  InvokeWithArgArray(soa, method, &arg_array, &result, shorty);
     [...]
557  return result;
558}

At line 540 is retrived the ArtMethod identified by the jmethodID passed as third argument, the FindVirtualMethod function is defined in reflection.cc The real method invocation is the call to InvokeWithArgArray at line 552.

The FindVirtualMethod function is used to look up a virtual method inside its receiver object, following box shows the code.

420static ArtMethod* FindVirtualMethod(mirror::Object* receiver, ArtMethod* method)
421    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
422  return receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(method, sizeof(void*));
423}

The above code calls function FindVirtualMethodForVirtualOrInterface (line 422) defined in class-inl.h to look up the target method. The following box show FindVirtualMethodForVirtualOrInterface code

399 inline ArtMethod* Class::FindVirtualMethodForVirtualOrInterface(ArtMethod* method,
400                                                                size_t pointer_size) {
401  if (method->IsDirect()) {
402    return method;
403  }
404  if (method->GetDeclaringClass()->IsInterface() && !method->IsMiranda()) {
405    return FindVirtualMethodForInterface(method, pointer_size);
406  }
407  return FindVirtualMethodForVirtual(method, pointer_size);
408 }

FindVirtualMethodForVirtual, defined in class-inl.h, returns the method scanning the vtable_ array.

387 inline ArtMethod* Class::FindVirtualMethodForVirtual(ArtMethod* method, size_t pointer_size) {
388  DCHECK(!method->GetDeclaringClass()->IsInterface() || method->IsMiranda());
389  // The argument method may from a super class.
390  // Use the index to a potentially overridden one for this instance's class.
391  return GetVTableEntry(method->GetMethodIndex(), pointer_size);
392 }

Analyzing the GetVtableEntry code, still defined in class-inl.h, we can see a call to function ShouldHaveEmbeddedImtAndVTable. If the returned result is True the vtable is embedded within the class, otherwise it is retrived calling the function GetVTable() (line 184)

180inline ArtMethod* Class::GetVTableEntry(uint32_t i, size_t pointer_size) {
181  if (ShouldHaveEmbeddedImtAndVTable()) {
182    return GetEmbeddedVTableEntry(i, pointer_size);
183  }
184  auto* vtable = GetVTable();
185  DCHECK(vtable != nullptr);
186  return vtable->GetElementPtrSize<ArtMethod*>(i, pointer_size);
187}

So, what do the function ShouldHaveEmbeddedImtAndVTable do?

754  bool ShouldHaveEmbeddedImtAndVTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
755    return IsInstantiable();
756  }

454  bool IsInstantiable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
455    return (!IsPrimitive() && !IsInterface() && !IsAbstract()) ||
456        (IsAbstract() && IsArrayClass());
457  }

The GetVTable code is showed in following box:

138 inline PointerArray* Class::GetVTable() {
139   DCHECK(IsResolved() || IsErroneous());
140   return GetFieldObject<PointerArray>(OFFSET_OF_OBJECT_MEMBER(Class, vtable_));
141 }

Ok now we know that virtual methods called by JNI are looked up using the vtable_ array (embedded or not).

Let’s me try to figure out how the runtime invoke methods called by reflection. The function involved in reflection calls is InvokeMethod defined in reflection.cc. The target method to invoke is looked up using the following code (line 600):

586  if (!m->IsStatic()) {
587    // Replace calls to String.<init> with equivalent StringFactory call.
588    if (declaring_class->IsStringClass() && m->IsConstructor()) {
589      jmethodID mid = soa.EncodeMethod(m);
590      m = soa.DecodeMethod(WellKnownClasses::StringInitToStringFactoryMethodID(mid));
591      CHECK(javaReceiver == nullptr);
592    } else {
593      // Check that the receiver is non-null and an instance of the field's declaring class.
594      receiver = soa.Decode<mirror::Object*>(javaReceiver);
595      if (!VerifyObjectIsClass(receiver, declaring_class)) {
596        return nullptr;
597      }
598
599      // Find the actual implementation of the virtual method.
600      m = receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(m, sizeof(void*));
601    }
602  }

Finally, the looked up method is invoked at line 640:

630  // Invoke the method.
631  JValue result;
632  uint32_t shorty_len = 0;
633  const char* shorty = np_method->GetShorty(&shorty_len);
634  ArgArray arg_array(shorty, shorty_len);
635  if (!arg_array.BuildArgArrayFromObjectArray(receiver, objects, np_method)) {
636    CHECK(soa.Self()->IsExceptionPending());
637    return nullptr;
638  }
639
640  InvokeWithArgArray(soa, m, &arg_array, &result, shorty);

Once we know how the runtime is lookuping virtual methods, we can exploit this mechanism to achieve Java virtual methods hooking.

Next post introduces ARTDroid, an easy-to-use library for hooking virtual methods calls under ART runtime.