分析安卓app类的加载和方法执行流程以及脱壳实战

[复制链接]
查看926 | 回复0 | 2024-12-8 16:54:11 | 显示全部楼层 |阅读模式

立即注册,突破游客限制,与技术大牛交流切磋,畅享海量资源,让你的技术之路不再孤单!

您需要 登录 才可以下载或查看,没有账号?立即注册

×
本帖最后由 洋咩咩 于 2024-12-8 16:56 编辑


1.前言
总结记录下ART环境下App启动流程中类加载流程以及相关知识,分析更深层次的脱壳点,加深对App加壳以及脱壳原理的理解。
环境:Android 8.0.0

2.相关知识总结
1.类加载流程
这里直接从loadClass开始,DexClassloader,PathClassloader,InMemoryDexClassLoader这三个类的父类是BaseDexClassLoader,BaseDexClassLoader的父类是Classloader,在ClassLoader这个类里实现了loadClass。

  1. public Class<?> loadClass(String name) throws ClassNotFoundException {
  2.        return loadClass(name, false);
  3.   }
复制代码



protected Class<?> loadClass(String name, boolean resolve),这里官方的源码注释解释的比较清楚了,首先确定这个类是否被加载过,没有的话会开始走双亲委派的流程调用parent.loadClass,如果父加载器都没有加载过,则自己调用findClass开始查找class并加载
  1. protected Class<?> loadClass(String name, boolean resolve)
  2.        throws ClassNotFoundException
  3. {
  4.            // First, check if the class has already been loaded
  5.            Class<?> c = findLoadedClass(name);
  6.            if (c == null) {
  7.                try {
  8.                    if (parent != null) {
  9.                        c = parent.loadClass(name, false);
  10.                   } else {
  11.                        c = findBootstrapClassOrNull(name);
  12.                    }
  13.                } catch (ClassNotFoundException e) {
  14.                    // ClassNotFoundException thrown if class not found
  15.                    // from the non-null parent class loader
  16.                }
  17.                if (c == null) {
  18.                    // If still not found, then invoke findClass in order
  19.                    // to find the class.
  20.                    c = findClass(name);
  21.                }
  22.            }
  23.            return c;
  24.    }
复制代码


findClass在BaseDexClassLoader中重写了
  1. @Override
  2. protected Class<?> findClass(String name) throws ClassNotFoundException {
  3.      List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
  4.      Class c = pathList.findClass(name, suppressedExceptions);
  5.      if (c == null) {
  6.          ClassNotFoundException cnfe = new ClassNotFoundException(
  7.                  "Didn't find class "" + name + "" on path: " + pathList);
  8.          for (Throwable t : suppressedExceptions) {
  9.              cnfe.addSuppressed(t);
  10.          }
  11.          throw cnfe;
  12.      }
  13.      return c;
  14. }
复制代码


DexPathList_public Class<?> findClass(String name, List<Throwable> suppressed) 这里的dexElements是在加载Dex时赋值的,保存的时当时加载的DexFile
  1. public Class<?> findClass(String name, List<Throwable> suppressed) {
  2.        for (Element element : dexElements) {
  3.            Class<?> clazz = element.findClass(name, definingContext, suppressed);
  4.            if (clazz != null) {
  5.                return clazz;
  6.            }
  7.        }
  8.        if (dexElementsSuppressedExceptions != null) {
  9.            suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
  10.        }
  11.        return null;
  12.    }
  13.    
复制代码


Element_public Class<?> findClass(String name, ClassLoader definingContext,List<Throwable> suppressed),这个Element类是DexPathList的一个内部类
  1. public Class<?> findClass(String name, ClassLoader definingContext,
  2.               List<Throwable> suppressed) {
  3.           return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
  4.                   : null;
  5.       }
  6.       
复制代码


DexFile_loadClassBinaryName
  1. public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {
  2.        return defineClass(name, loader, mCookie, this, suppressed);
  3.    }
  4.    
复制代码


DexFile_defineClass
  1. private static Class defineClass(String name, ClassLoader loader, Object cookie,
  2.                                  DexFile dexFile, List<Throwable> suppressed) {
  3.     Class result = null;
  4.     try {
  5.         result = defineClassNative(name, loader, cookie, dexFile);
  6.     } catch (NoClassDefFoundError e) {
  7.         if (suppressed != null) {
  8.             suppressed.add(e);
  9.         }
  10.     } catch (ClassNotFoundException e) {
  11.         if (suppressed != null) {
  12.             suppressed.add(e);
  13.         }
  14.     }
  15.     return result;
  16. }
复制代码


DexFile_defineClassNative 这里进入了native层的DexFile中,这段代码在进行DexFile的注册。
  1. static jclass DexFile_defineClassNative(JNIEnv* env,jclass,jstring javaName,jobject javaLoader,                                        jobject cookie,jobject dexFile) {
  2. ...
  3.   for (auto& dex_file : dex_files) {
  4.     const DexFile::ClassDef* dex_class_def =
  5.         OatDexFile::FindClassDef(*dex_file, descriptor.c_str(), hash);
  6.     if (dex_class_def != nullptr) {
  7.       ScopedObjectAccess soa(env);
  8.       ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
  9.       StackHandleScope<1> hs(soa.Self());
  10.       Handle<mirror::ClassLoader> class_loader(
  11.           hs.NewHandle(soa.Decode<mirror::ClassLoader>(javaLoader)));
  12.       ObjPtr<mirror::DexCache> dex_cache =
  13.           class_linker->RegisterDexFile(*dex_file, class_loader.Get());
  14.       if (dex_cache == nullptr) {
  15.         // OOME or InternalError (dexFile already registered with a different class loader).
  16.         soa.Self()->AssertPendingException();
  17.         return nullptr;
  18.       }
  19.       ObjPtr<mirror::Class> result = class_linker->DefineClass(soa.Self(),descriptor.c_str(),hash,class_loader,*dex_file,*dex_class_def);
  20.       // Add the used dex file. This only required for the DexFile.loadClass API since normal
  21.       // class loaders already keep their dex files live.
  22.       class_linker->InsertDexFileInToClassLoader(soa.Decode<mirror::Object>(dexFile),class_loader.Get());
  23.       if (result != nullptr) {
  24.         VLOG(class_linker) << "DexFile_defineClassNative returning " << result
  25.                            << " for " << class_name.c_str();
  26.         return soa.AddLocalReference<jclass>(result);
  27.       }
  28.     }
  29.   }
  30.   VLOG(class_linker) << "Failed to find dex_class_def " << class_name.c_str();
  31.   return nullptr;
  32. }
复制代码


__mirror::Class* ClassLinker:efineClass__这里也是在初始化类,分配类空间等,有三个阶段SetupClass,LoadClass,LinkClass,关于类加载主要看LoadClass主要作用是将类的字段和方法信息从Dex文件中加载到内存中,并建立相应的数据结构。
  1. mirror::Class* ClassLinker::DefineClass(Thread* self,const char* escriptor,size_t hash,Handle<mirrr::ClassLoader> class_loader,const DexFile& dex_file,const DexFile::ClassDef dex_class_def) {
  2. ...
  3. klass->SetDexCache(dex_cache);
  4. SetupClass(*new_dex_file, *new_class_def, klass, class_loader.Get());
  5. ...
  6.   // Load the fields and other things after we arenserted in the table. This is so that we don't
  7.   // end up allocating unfree-able linear alloc rources and then lose the race condition. The
  8.   // other reason is that the field roots are onlvisited from the class table. So we need to be
  9.   // inserted before we allocate / fill in these field
  10.   LoadClass(self, *new_dex_file, *new_class_def, klass);
  11. ...
  12. if (!LinkClass(self, descriptor, klass, interfaces, &h_new_class)) {
  13.     // Linking failed.
  14.     if (!klass->IsErroneous()) {
  15.       mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self);
  16.     }
  17.     return nullptr;
  18.   }
  19. ...
复制代码


ClassLinker:oadClass
  1. void ClassLinker::LoadClass(Thread* self,
  2.                             const DexFile& dex_file,
  3.                             const DexFile::ClassDef& dex_class_def,
  4.                             Handle<mirror::Class> klass) {
  5.   const uint8_t* class_data = dex_file.GetClassData(dex_class_def);
  6.   if (class_data == nullptr) {
  7.     return;  // no fields or methods - for example a marker interface
  8.   }
  9.   LoadClassMembers(self, dex_file, class_data, klass);
  10. }
复制代码


ClassLinker:oadClassMembers 这里开始加载类的成员变量,方法,方法的加载有两步LoadMethod和LinkCode。
  1. void ClassLinker::LoadClassMembers(Thread* self,const DexFile& dex_file,const uint8_t* class_data,Handle<mirror::Class> klass) {
  2.   {    // Note: We cannot have thread suspension until the field and method arrays are setup or else
  3.     // Class::VisitFieldRoots may miss some fields or methods.
  4.     ScopedAssertNoThreadSuspension nts(__FUNCTION__);
  5.     // Load static fields.
  6. ...
  7.     for (; it.HasNextStaticField(); it.Next()) {
  8.       uint32_t field_idx = it.GetMemberIndex();
  9.       DCHECK_GE(field_idx, last_field_idx);  // Ordering enforced by DexFileVerifier.
  10.       if (num_sfields == 0 || LIKELY(field_idx > last_field_idx)) {
  11.         DCHECK_LT(num_sfields, it.NumStaticFields());
  12.         LoadField(it, klass, &sfields->At(num_sfields));
  13.         ++num_sfields;
  14.         last_field_idx = field_idx;
  15.       }
  16.     }
  17.     // Load instance fields.
  18.     LengthPrefixedArray<ArtField>* ifields = AllocArtFieldArray(self,allocator,it.NumInstanceFields());
  19.     size_t num_ifields = 0u;
  20.     last_field_idx = 0u;
  21.     for (; it.HasNextInstanceField(); it.Next()) {
  22.       uint32_t field_idx = it.GetMemberIndex();
  23.       DCHECK_GE(field_idx, last_field_idx);  // Ordering enforced by DexFileVerifier.
  24.       if (num_ifields == 0 || LIKELY(field_idx > last_field_idx)) {
  25.         DCHECK_LT(num_ifields, it.NumInstanceFields());
  26.         LoadField(it, klass, &ifields->At(num_ifields));
  27.         ++num_ifields;
  28.         last_field_idx = field_idx;
  29.       }
  30.     }
  31. ...
  32.     // Set the field arrays.
  33.     klass->SetSFieldsPtr(sfields);
  34.     DCHECK_EQ(klass->NumStaticFields(), num_sfields);
  35.     klass->SetIFieldsPtr(ifields);
  36.     DCHECK_EQ(klass->NumInstanceFields(), num_ifields);
  37.     // Load methods.
  38.     bool has_oat_class = false;
  39.     const OatFile::OatClass oat_class =
  40.         (Runtime::Current()->IsStarted() && !Runtime::Current()->IsAotCompiler())
  41.             ? OatFile::FindOatClass(dex_file, klass->GetDexClassDefIndex(), &has_oat_class)
  42.             : OatFile::OatClass::Invalid();
  43.     const OatFile::OatClass* oat_class_ptr = has_oat_class ? &oat_class : nullptr;
  44.     klass->SetMethodsPtr(
  45.         AllocArtMethodArray(self, allocator, it.NumDirectMethods() + it.NumVirtualMethods()),
  46.         it.NumDirectMethods(),
  47.         it.NumVirtualMethods());
  48.     size_t class_def_method_index = 0;
  49.     uint32_t last_dex_method_index = DexFile::kDexNoIndex;
  50.     size_t last_class_def_method_index = 0;
  51.     // TODO These should really use the iterators.
  52.     for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) {
  53.       ArtMethod* method = klass->GetDirectMethodUnchecked(i, image_pointer_size_);
  54.       LoadMethod(dex_file, it, klass, method);
  55.       LinkCode(this, method, oat_class_ptr, class_def_method_index);
  56.       uint32_t it_method_index = it.GetMemberIndex();
  57.       if (last_dex_method_index == it_method_index) {
  58.         // duplicate case
  59.         method->SetMethodIndex(last_class_def_method_index);
  60.       } else {
  61.         method->SetMethodIndex(class_def_method_index);
  62.         last_dex_method_index = it_method_index;
  63.         last_class_def_method_index = class_def_method_index;
  64.       }
  65.       class_def_method_index++;
  66.     }
  67.     for (size_t i = 0; it.HasNextVirtualMethod(); i++, it.Next()) {
  68.       ArtMethod* method = klass->GetVirtualMethodUnchecked(i, image_pointer_size_);
  69.       LoadMethod(dex_file, it, klass, method);
  70.       DCHECK_EQ(class_def_method_index, it.NumDirectMethods() + i);
  71.       LinkCode(this, method, oat_class_ptr, class_def_method_index);
  72.       class_def_method_index++;
  73.     }
  74.     DCHECK(!it.HasNext());
  75.   }
  76.   // Ensure that the card is marked so that remembered sets pick up native roots.
  77.   Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(klass.Get());
  78.   self->AllowThreadSuspension();
  79. }
复制代码


ClassLinker:oadMethod LoadMethod为ArtMethod的基础属性赋值,并检查是否是构造函数,这里可以看到在设置CodeItemOffset,以前的函数抽取壳会在这个阶段回填字节码,通过hook LoadMethod即可脱到完整的Dex。
  1. void ClassLinker::LoadMethod(const DexFile& dex_file,const ClassDataItemIterator& it,Handle<mirror::Class> klass,ArtMethod* dst) {
  2.   uint32_t dex_method_idx = it.GetMemberIndex();
  3.   const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx);
  4.   const char* method_name = dex_file.StringDataByIdx(method_id.name_idx_);
  5.   ScopedAssertNoThreadSuspension ants("LoadMethod");
  6.   dst->SetDexMethodIndex(dex_method_idx);
  7.   dst->SetDeclaringClass(klass.Get());
  8.   dst->SetCodeItemOffset(it.GetMethodCodeItemOffset());
  9.   dst->SetDexCacheResolvedMethods(klass->GetDexCache()->GetResolvedMethods(), image_pointer_size_);
  10.   uint32_t access_flags = it.GetMethodAccessFlags();
  11.   if (UNLIKELY(strcmp("finalize", method_name) == 0)) {
  12.   } else if (method_name[0] == '<') {
  13.     // Fix broken access flags for initializers. Bug 11157540.
  14.     bool is_init = (strcmp("<init>", method_name) == 0);
  15.     bool is_clinit = !is_init && (strcmp("<clinit>", method_name) == 0);
  16.     if (UNLIKELY(!is_init && !is_clinit)) {
  17.       LOG(WARNING) << "Unexpected '<' at start of method name " << method_name;
  18.     } else{
  19.     ...
  20. }
  21.   }
  22.   dst->SetAccessFlags(access_flags);
  23. }
复制代码


ClassLinker:inkCode LinkCode的主要工作是把,以及一些相关操作例如分配空间,关联引用等,还有Native函数的绑定,最后会调用SetEntryPointFromQuickCompiledCode设置方法被调用时的入口,method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge())这段代码就是当oat被阻断后会把方法入口设置为解释执行入口。
  1. static void LinkCode(ClassLinker* class_linker,ArtMethod* method,const OatFile::OatClass* oat_class,uint32_t class_def_method_index) REQUIRES_SHARED(Locks::mutator_lock_) {
  2.   Runtime* const runtime = Runtime::Current();
  3.   if (runtime->IsAotCompiler()) {
  4.     // The following code only applies to a non-compiler runtime.
  5.     return;
  6.   }
  7.   // Method shouldn't have already been linked.
  8.   DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr);
  9.   if (oat_class != nullptr) {
  10.     // Every kind of method should at least get an invoke stub from the oat_method.
  11.     // non-abstract methods also get their code pointers.
  12.     const OatFile::OatMethod oat_method = oat_class->GetOatMethod(class_def_method_index);
  13.     oat_method.LinkMethod(method);
  14.   }
  15.   // Install entry point from interpreter.
  16.   const void* quick_code = method->GetEntryPointFromQuickCompiledCode();
  17.   bool enter_interpreter = class_linker->ShouldUseInterpreterEntrypoint(method, quick_code);
  18.   if (!method->IsInvokable()) {
  19.     EnsureThrowsInvocationError(class_linker, method);
  20.     return;
  21.   }
  22.   if (method->IsStatic() && !method->IsConstructor()) {
  23.     // For static methods excluding the class initializer, install the trampoline.
  24.     // It will be replaced by the proper entry point by ClassLinker::FixupStaticTrampolines
  25.     // after initializing class (see ClassLinker::InitializeClass method).
  26.     method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionStub());
  27.   } else if (quick_code == nullptr && method->IsNative()) {
  28.     method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub());
  29.   } else if (enter_interpreter) {
  30.     // Set entry point from compiled code if there's no code or in interpreter only mode.
  31.     method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
  32.   }
  33.   if (method->IsNative()) {
  34.     // Unregistering restores the dlsym lookup stub.
  35.     method->UnregisterNative();
  36.     if (enter_interpreter || quick_code == nullptr) {
  37.       // We have a native method here without code. Then it should have either the generic JNI
  38.       // trampoline as entrypoint (non-static), or the resolution trampoline (static).
  39.       // TODO: this doesn't handle all the cases where trampolines may be installed.
  40.       const void* entry_point = method->GetEntryPointFromQuickCompiledCode();
  41.       DCHECK(class_linker->IsQuickGenericJniStub(entry_point) ||
  42.              class_linker->IsQuickResolutionStub(entry_point));
  43.     }
  44.   }
  45. }
复制代码


2.方法执行
类加载完成之后,一般就要调用类的方法了,所以继续看一下方法执行的流程,Android 8.0中主要是两种方法执行方式,解释执行和OAT的快速执行,解释执行主要是根据字节码执行方法,快速执行通过OAT中预先编译成的本地机器码来执行方法,对于加壳来说,阻断了OAT流程后系统会转向解释执行。

在安卓中,Java 函数可以通过两种主要的方式进行调用。一种是通过自然的代码执行路径进行调用,即在 Java 虚拟机中直接调用,另一种是通过 JNI进行调用,即通过本地代码与 Java 代码进行交互来实现函数调用,先说JNI调用的流程。
JNI调用Java函数主要是通过一系列的CallXXXMethod(+V代表无返回值+A代表参数以数组形式传递)JNI函数,这里找几个看一下实现方式,可以看到都调用了InvokeVirtualOrInterfaceWithVarArgs。

  1. static jobject CallObjectMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
  2.     va_list ap;
  3.     va_start(ap, mid);
  4.     CHECK_NON_NULL_ARGUMENT(obj);
  5.     CHECK_NON_NULL_ARGUMENT(mid);
  6.     ScopedObjectAccess soa(env);
  7.     JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap));
  8.     va_end(ap);
  9.     return soa.AddLocalReference<jobject>(result.GetL());
  10.   }
  11.   static jboolean CallBooleanMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
  12.     va_list ap;
  13.     va_start(ap, mid);
  14.     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
  15.     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
  16.     ScopedObjectAccess soa(env);
  17.     JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap));
  18.     va_end(ap);
  19.     return result.GetZ();
  20.   }
  21.   
复制代码


InvokeVirtualOrInterfaceWithVarArgs
  1. JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,
  2.                                            jobject obj, jmethodID mid, va_list args) {
  3.                                             ...
  4.   ObjPtr<mirror::Object> receiver = soa.Decode<mirror::Object>(obj);
  5.   ArtMethod* method = FindVirtualMethod(receiver, jni::DecodeArtMethod(mid));
  6.   bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
  7.   if (is_string_init) {
  8.     // Replace calls to String.<init> with equivalent StringFactory call.
  9.     method = WellKnownClasses::StringInitToStringFactory(method);
  10.     receiver = nullptr;
  11.   }
  12.   uint32_t shorty_len = 0;
  13.   const char* shorty =
  14.       method->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetShorty(&shorty_len);
  15.   JValue result;
  16.   ArgArray arg_array(shorty, shorty_len);
  17.   arg_array.BuildArgArrayFromVarArgs(soa, receiver, args);
  18.   InvokeWithArgArray(soa, method, &arg_array, &result, shorty);
  19.   ...
  20.   return result;
  21. }
复制代码


InvokeWithArgArray
  1. static void InvokeWithArgArray(const ScopedObjectAccessAlreadyRunnable& soa,
  2.                                ArtMethod* method, ArgArray* arg_array, JValue* result,
  3.                                const char* shorty)
  4.     REQUIRES_SHARED(Locks::mutator_lock_) {
  5.   uint32_t* args = arg_array->GetArray();
  6.   if (UNLIKELY(soa.Env()->check_jni)) {
  7.     CheckMethodArguments(soa.Vm(), method->GetInterfaceMethodIfProxy(kRuntimePointerSize), args);
  8.   }
  9.   method->Invoke(soa.Self(), args, arg_array->GetNumBytes(), result, shorty);
  10. }
复制代码


ArtMethod::invoke
  1. void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result,
  2.                        const char* shorty) {
  3. ..
  4.   // Push a transition back into managed code onto the linked list in thread.
  5.   ManagedStack fragment;
  6.   self->PushManagedStackFragment(&fragment);
  7.   Runtime* runtime = Runtime::Current();
  8.   if (UNLIKELY(!runtime->IsStarted() || Dbg::IsForcedInterpreterNeededForCalling(self, this))) {
  9.     if (IsStatic()) {
  10.       art::interpreter::EnterInterpreterFromInvoke(
  11.           self, this, nullptr, args, result, /*stay_in_interpreter*/ true);
  12.     } else {
  13.       mirror::Object* receiver =
  14.           reinterpret_cast<StackReference<mirror::Object>*>(&args[0])->AsMirrorPtr();
  15.       art::interpreter::EnterInterpreterFromInvoke(
  16.           self, this, receiver, args + 1, result, /*stay_in_interpreter*/ true);
  17.     }
  18.   } else {
  19.     DCHECK_EQ(runtime->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
  20.     constexpr bool kLogInvocationStartAndReturn = false;
  21.     bool have_quick_code = GetEntryPointFromQuickCompiledCode() != nullptr;
  22.     if (LIKELY(have_quick_code)) {
  23.       if (kLogInvocationStartAndReturn) {
  24.         LOG(INFO) << StringPrintf(
  25.             "Invoking '%s' quick code=%p static=%d", PrettyMethod().c_str(),
  26.             GetEntryPointFromQuickCompiledCode(), static_cast<int>(IsStatic() ? 1 : 0));
  27.       }
  28.       // Ensure that we won't be accidentally calling quick compiled code when -Xint.
  29.       if (kIsDebugBuild && runtime->GetInstrumentation()->IsForcedInterpretOnly()) {
  30.         CHECK(!runtime->UseJitCompilation());
  31.         const void* oat_quick_code =         (IsNative() || !IsInvokable() || IsProxyMethod() || IsObsolete())
  32.             ? nullptr
  33.             : GetOatMethodQuickCode(runtime->GetClassLinker()->GetImagePointerSize());
  34.         CHECK(oat_quick_code == nullptr || oat_quick_code != GetEntryPointFromQuickCompiledCode())
  35.             << "Don't call compiled code when -Xint " << PrettyMethod();
  36.       }
  37.       if (!IsStatic()) {
  38.         (*art_quick_invoke_stub)(this, args, args_size, self, result, shorty);
  39.       } else {
  40.         (*art_quick_invoke_static_stub)(this, args, args_size, self, result, shorty);
  41.       }}
  42.       ...
  43.   // Pop transition.
  44.   self->PopManagedStackFragment(fragment);
  45. }
复制代码


这就到了比较关键的地方,通过反射和JNI调用都会走到这里,Invoke会判断方法调用的环境,方法类型,如果不是静态方法会进入art_quick_invoke_stub若目标方法已编译为快速路径,则经 INVOKE_STUB_CALL_AND_RETURN的汇编代码后,通过 PtrSizedFields 中的 entry_point_from_quick_compiled_code_ 跳转到目标方法的入口(entry_point_from_quick_compiled_code_ 也是自然通过代码执行Java代码的入口)。
这里有两种入口,一种是编译好的本地机器指令入口直接开始执行,另一种就是解释执行入口,在LinkCode函数说到当OAT被阻断后会调用method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge()),这样的话entry_point_from_quick_compiled_code_ 所指向的就是art_quick_to_interpreter_bridge这个桥函数了,这个桥函数也是汇编实现。

  1. ENTRY art_quick_to_interpreter_bridge
  2.     SETUP_SAVE_REFS_AND_ARGS_FRAME         // Set up frame and save arguments.
  3.     //  x0 will contain mirror::ArtMethod* method.
  4.     mov x1, xSELF                          // How to get Thread::Current() ???
  5.     mov x2, sp
  6.     // uint64_t artQuickToInterpreterBridge(mirror::ArtMethod* method, Thread* self,
  7.     //                                      mirror::ArtMethod** sp)
  8.     bl   artQuickToInterpreterBridge
  9.     RESTORE_SAVE_REFS_AND_ARGS_FRAME       // TODO: no need to restore arguments in this case.
  10.     fmov d0, x0
  11.     RETURN_OR_DELIVER_PENDING_EXCEPTION
  12. END art_quick_to_interpreter_bridge
复制代码


通过bl跳转到了artQuickToInterpreterBridge函数,之后的调用流程是artQuickToInterpreterBridge() → interpreter::EnterInterpreterFromEntryPoint() → interpreter::Execute()。

interpreter::Execute()
  1. static inline JValue Execute(
  2.     Thread* self,
  3.     const DexFile::CodeItem* code_item,
  4.     ShadowFrame& shadow_frame,
  5.     JValue result_register,
  6.     bool stay_in_interpreter = false) REQUIRES_SHARED(Locks::mutator_lock_) {
  7.   DCHECK(!shadow_frame.GetMethod()->IsAbstract());
  8.   DCHECK(!shadow_frame.GetMethod()->IsNative());
  9. ...
  10.     if (kInterpreterImplKind == kMterpImplKind) {
  11.       if (transaction_active) {
  12.         // No Mterp variant - just use the switch interpreter.
  13.         return ExecuteSwitchImpl<false, true>(self, code_item, shadow_frame, result_register,
  14.                                               false);
  15.       ...
  16.       }
  17.     } else {
  18.       DCHECK_EQ(kInterpreterImplKind, kSwitchImplKind);
  19.       if (transaction_active) {
  20.         return ExecuteSwitchImpl<false, true>(self, code_item, shadow_frame, result_register,
  21.                                               false);
  22.       } else {
  23.         return ExecuteSwitchImpl<false, false>(self, code_item, shadow_frame, result_register,
  24.                                                false);
  25.       }
  26.     }
  27.   }
  28. ...
  29. }
复制代码


解释执行会通过kInterpreterImplKind的类型来判断执行哪种解释器,解释执行总的来说有三种方式goto型,汇编型和switch型,这三种的实现方式其实就和字面意思差不多,分别使用goto,汇编,switch来完成对字节码匹配执行,goto型在较早的版本还有,在本次分析的安卓8系统中已经不存在了,安卓8实现了汇编型和switch型,默认使用汇编型。
  1. enum InterpreterImplKind {
  2.   kSwitchImplKind,        // Switch-based interpreter implementation.
  3.   kMterpImplKind          // Assembly interpreter
  4. };
  5. static constexpr InterpreterImplKind kInterpreterImplKind = kMterpImplKind;
复制代码


3.脱壳实战
上面分析了类的加载流程和Java方法的调用流程,这里面大量出现了Dexfile,ArtMethod,两个地方都是可以用于脱壳,通过Dexfile可以直接dump Dexfile,而通过ArtMethod也可以间接获取到Dexfile对象,这里随便找一个,loadClassMember方法进行实战,还是用的frida hook的方式。
这里代码用上次的改一下即可
  1. function hookartLoadClassMember() {
  2.     var addrLoadClassMember = null;
  3.     var symbols = Module.enumerateSymbolsSync("libart.so");
  4.     for (var i = 0; i < symbols.length; i++) {
  5.         var symbol = symbols[i];
  6.        if (symbol.name.indexOf("ClassLinker") >= 0
  7.             && symbol.name.indexOf("DexFile") >= 0
  8.             && symbol.name.indexOf("LoadClassMember")>=0) {
  9.             addrLoadClassMember = symbol.address;
  10.             break;
  11.         }
  12.     }
  13.     if (addrLoadClassMember != null) {  
  14.         Interceptor.attach(addrLoadClassMember, {
  15.             onEnter: function (args) {
  16.                 this.dexfileptr = args[2];
  17.             },
  18.             onLeave: function (retval) {
  19.                 var dexfilebegin = null;
  20.                 var dexfilesize = null;
  21.                 if (this.dexfileptr != null) {
  22.                     dexfilebegin = Memory.readPointer(ptr(this.dexfileptr).add(Process.pointerSize * 1));
  23.                     dexfilesize = Memory.readU32(ptr(this.dexfileptr).add(Process.pointerSize * 2));
  24.                     var dexfile_path = savepath + "/" + dexfilesize + "_LoadClassMember.dex";
  25.                     var dexfile_handle = null;
  26.                     try {
  27.                         dexfile_handle = new File(dexfile_path, "r");
  28.                         if (dexfile_handle && dexfile_handle != null) {
  29.                             dexfile_handle.close()
  30.                         }
  31.                     } catch (e) {
  32.                         dexfile_handle = new File(dexfile_path, "a+");
  33.                         if (dexfile_handle && dexfile_handle != null) {
  34.                             var dex_buffer = ptr(dexfilebegin).readByteArray(dexfilesize);
  35.                             dexfile_handle.write(dex_buffer);
  36.                             dexfile_handle.flush();
  37.                             dexfile_handle.close();
  38.                             console.log("[dumpdex]:", dexfile_path);
  39.                         }
  40.                     }
  41.                 }
  42.                 var dexfileobj = new DexFile(dexfilebegin, dexfilesize);
  43.                 if (dex_maps[dexfilebegin] == undefined) {
  44.                     dex_maps[dexfilebegin] = dexfilesize;
  45.                     console.log("got a dex:", dexfilebegin, dexfilesize);
  46.                 }
  47.             }
  48.         });
  49.     }
  50. }
复制代码

截图202412081654019388.png

这个人还没有签名
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则