Android源码学习

#本人水平有限,大佬发现文章问题请批评改正#

PathClassLoader

PathClassLoader(String dexPath, ClassLoader parent)

        Dalvik调用链:ApplicationLoaders (new)-> pathClassloader (继承) -> BaseDexClassLoader (new) -> DexPathList (构造中调用)-> makeDexElements (调用)-> loadDexFile (new或调用)-> DexFile或loadDex (调用)-> openDexFile (调用)-> openDexFileNative (调用)-> dvmRawDexFileOpen (调用)-> dvmOptimizeDexFile /*dvm优化Dex文件*/ fork子进程,获取root权限以拼接出路径 /system/bin/dexopt,启动dexopt程序 (调用)-> fromDex (调用)-> 1.dvmPrepForDexOpt(启动一个虚拟机进程) 2.dvmContinueOptimization(干活) (调用)-> rewriteDex(找不到后面了) 最后调用dvmRawDexFileOpen中的dvmOptimizeDexFile方法将优化后dex映射进内存。

        Dalvik加载dex过程其实就是将一个类转换为ClassObject的一个实例化对象,将ClassObject添加到全局变量gDvm中的loadedClasses成员中;执行时通过dvmInterpret初始化解释器,执行字节码指令。

        loadDexFile:判断是否有优化路径,没有则生成,有则直接使用优化路径调用loadDex,就是APP是否是第一次运行。

05-DALVIK加载和解析DEX过程 - Domefy - 博客园 (cnblogs.com)


ArtMethod结构 :android-9.0.0_r30

class ArtMethod {
 …………
protect:
 
 // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
  // The class we are a part of.
  //GCroot添加在N,实际是个指针,这个字段即是表明所属类,一般用喜闻乐见的uint32_t代替
  GcRoot<mirror::Class> declaring_class_;
 
  // Access flags; low 16 bits are defined by spec.
  // Getting and setting this flag needs to be atomic when concurrency is
  // possible, e.g. after this method's class is linked. Such as when setting
  // verifier flags and single-implementation flag.
  //可以理解为该函数的标志位,如函数为public,private,static,native等。
  //这个字段java2native,xposed hook的标志位。
  //值得一提的是,9.0以后的Hid API,对应函数GetHiddenApiAccessFlags,不是这个,但是看到这里,可以考虑如何修改hide标志位
 
  std::atomic<std::uint32_t> access_flags_;
 
 
  /* Dex file fields. The defining dex file is available via declaring_class_->dex_cache_ */
  // Offset to the CodeItem.
  //加固代码替换点。
  //里面指向code_item指针,code_item存储的实际是dex当中的字节码.其用处本来是适配dalvik解释器,即无法编译成机器码的,用解释器来执行。
 //
  uint32_t dex_code_item_offset_;
 
 
 
  // Index into method_ids of the dex file associated with this method.
  //主要作为寻址替换用
  uint32_t dex_method_index_;
 
 
  /* End of dex file fields. */
  // Entry within a dispatch table for this method. For static/direct methods the index is into
  // the declaringClass.directMethods, for virtual methods the vtable and for interface methods the
  // ifTable.
  uint16_t method_index_;
 
 
 
  // The hotness we measure for this method. Not atomic, as we allow
  // missing increments: if the method is hot, we will see it eventually.
  //方法被记录的频率热度,用在Android N+混合编译部分,根据该值来判断是否使用JIT编译。
  uint16_t hotness_count_;
 
 
  // Fake padding field gets inserted here.
  // Must be the last fields in the method.
   //没错,这个Fields9.0地址又改了,需适配,在下面详细介绍每个字段
  struct PtrSizedFields {
    // Depending on the method type, the data is
    //   - native method: pointer to the JNI function registered to this method
    //                    or a function to resolve the JNI function,
    //   - conflict method: ImtConflictTable,
    //   - abstract/interface method: the single-implementation if any,
    //   - proxy method: the original interface method or constructor,
    //   - other methods: the profiling data.
 
 
   //这个字段顾名思义,根据方法类型,会充当不同作用,是不是可以理解为以前几个指针的复合……
    void* data_;
    // Method dispatch from quick compiled code invokes this pointer which may cause bridging into
    // the interpreter.
    void* entry_point_from_quick_compiled_code_;
  } ptr_sized_fields_;
 
  …………
  }

DexClassLoader

目标:基于frida写一个dexdump

        在 Android 系统中,每个加载的 DEX(Dalvik Executable)文件都会在内存中被添加至一个叫做「DexPathList」的链表,用于存储加载的 DEX 文件的信息。这个链表是由类加载器(ClassLoader)的实现类「PathClassLoader」或「DexClassLoader」维护的,其中「DexPathList」是其内部类。当加载一个包含 DEX 文件的 APK 或者其它形式的 DEX 文件时,类加载器会将这些 DEX 文件添加到「DexPathList」链表中。

        从DexClassLoader源码往里面跟,第一个关键地方 makeDexElements() ;该函数判断传入路径内是否存在 以 .dex 结尾文件,之后通过 loadDexFile 函数尝试加载dex文件,加载过后返回DexFile 对象,之后再将该dex加入由系统维护的 elements 数组中。

private static Element[] makeDexElements(List<File> files, File optimizedDirectory,
            List<IOException> suppressedExceptions, ClassLoader loader, boolean isTrusted) {
      Element[] elements = new Element[files.size()];
      int elementsPos = 0;
      /*
       * Open all files and load the (direct or contained) dex files up front.
       */
      for (File file : files) {
          if (file.isDirectory()) {
              // We support directories for looking up resources. Looking up resources in
              // directories is useful for running libcore tests.
              elements[elementsPos++] = new Element(file);
          } else if (file.isFile()) {
              String name = file.getName();

              DexFile dex = null;
              if (name.endsWith(DEX_SUFFIX)) {
                  // Raw dex file (not inside a zip/jar).
                  try {
                      dex = loadDexFile(file, optimizedDirectory, loader, elements);
                      if (dex != null) {
                          elements[elementsPos++] = new Element(dex, null);
                      }
                  } catch (IOException suppressed) {
                      System.logE("Unable to load dex file: " + file, suppressed);
                      suppressedExceptions.add(suppressed);
                  }
              } else {
                  try {
                      dex = loadDexFile(file, optimizedDirectory, loader, elements);
                  } catch (IOException suppressed) {
                      /*
                       * IOException might get thrown "legitimately" by the DexFile constructor if
                       * the zip file turns out to be resource-only (that is, no classes.dex file
                       * in it).
                       * Let dex == null and hang on to the exception to add to the tea-leaves for
                       * when findClass returns null.
                       */
                      suppressedExceptions.add(suppressed);
                  }

                  if (dex == null) {
                      elements[elementsPos++] = new Element(file);
                  } else {
                      elements[elementsPos++] = new Element(dex, file);
                  }
              }
              if (dex != null && isTrusted) {
                dex.setTrusted();
              }
          } else {
              System.logW("ClassLoader referenced unknown path: " + file);
          }
      }
      if (elementsPos != elements.length) {
          elements = Arrays.copyOf(elements, elementsPos);
      }
      return elements;
    }

so文件中的 fork 函数

int sub_83DC()
{
  int i; // r0
  __pid_t v1; // r5
  __pid_t v2; // r0
  FILE *v4; // r5
  int v5; // r8
  __pid_t pid; // [sp+4h] [bp-194h]
  char v7[10]; // [sp+8h] [bp-190h] BYREF
  char v8[118]; // [sp+12h] [bp-186h] BYREF
  char s[128]; // [sp+88h] [bp-110h] BYREF
  char format[128]; // [sp+108h] [bp-90h] BYREF
  int v11; // [sp+188h] [bp-10h]

  qmemcpy(format, &unk_F130, sizeof(format));
  format[0] = 47;
  for ( i = 1; i != 128; ++i )
    format[i] ^= 0xE9u;
  v1 = getpid();
  sprintf(s, format, v1);
  v2 = fork();/*此时在父进程中,fork()函数为 #创建自身进程副本# 所以子进程要执行的代码
与父进程是相同的,Linux系统会通过不同的fork函数的返回值帮助我们编写子进程与父进程的不同代码
fork函数在父进程中的返回值为新建的子进程的pid,但是在子进程中的返回值为0,
所以我们可以通过判断fork函数的返回值来区别子进程与父进程的执行逻辑*/
  
  if ( !v2 )   //在子进程中返回值为0,!0为真,所以在子进程中会执行if中的内容
  {
    pid = v1;
    if ( v1 == getppid() )
    {
      ptrace(PTRACE_TRACEME);
      v4 = fopen(s, &format[16]);
      if ( v4 )
      {
        while ( 1 )
        {
          while ( !fgets(v7, 128, v4) )
          {
LABEL_11:
            sleep(2u);
            v4 = fopen(s, &format[16]);
            if ( !v4 )
              goto LABEL_12;
          }
          if ( !strncmp(v7, &format[18], 9u) )
          {
            v5 = atoi(v8);
            fclose(v4);
            if ( v5 )
              break;
            goto LABEL_11;
          }
        }
      }
LABEL_12:
      kill(pid, 9);
    }
LABEL_13:
    exit(1);
  }
  
  //在父进程中返回子进程的pid,进不去if判断,所以从这里开始执行
  if ( v2 == -1 )
    goto LABEL_13;
  return _stack_chk_guard - v11;
}

linker加载ELF文件过程(自执行类型)

ps:mlbz,看错了,分析了一半多点,对付看吧

环境:ida7.6      Android 9   piexl 3 

linker主函数:入口点 __linker_init()函数

extern "C" ElfW(Addr) __linker_init(void* raw_args) {
    KernelArgumentBlock args(raw_args);
  

    static uintptr_t linktime_addr = reinterpret_cast<uintptr_t>(&linktime_addr);
	//linker文件在内存中实际映射的基地址,在Android 7之前,linker_addr是通过是直接从ELF辅助向量中读取AT_BASE获得。Android 8之后,通过定义静态变量linktime_addr是来计算linker_addr。
	//ida动态调试时代码:v10 = &_dl__ZZ13__linker_initE13linktime_addr - _dl__ZZ13__linker_initE13linktime_addr;
    ElfW(Addr) linker_addr = reinterpret_cast<uintptr_t>(&linktime_addr) - linktime_addr;
  
  #if defined(__clang_analyzer__)
    linker_addr += reinterpret_cast<uintptr_t>(raw_args);
  #endif
  
    //读取辅助向量,返回内核加载ELF文件的起始地址
    ElfW(Addr) entry_point = args.getauxval(AT_ENTRY);
	//解析ELF的hdr和phdr
    ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)*>(linker_addr);
    ElfW(Phdr)* phdr = reinterpret_cast<ElfW(Phdr)*>(linker_addr + elf_hdr->e_phoff);
    
	//创建soinfo对象,每个由linker加载到内存的ELF,都会有一个soinfo对象表示,同一个动态库dlopen两次会创建两个soinfo对象,且文件会被映射到不同的内存中
	//此时创建的soinfo对象保存在栈中,后面会使用get_libdl_info()函数在堆中分配一个soinfo对象,在放入系统solist中
	//ida func:_dl__ZN6soinfoC1EP19android_namespace_tPKcPK4statli
    soinfo linker_so(nullptr, nullptr, nullptr, 0, 0);
    
	//linker的ELF文件格式解析
    linker_so.base = linker_addr;
	//传入program_header_table地址与header table的数量,计算整个PT_LOAD节的长度之和
    linker_so.size = phdr_table_get_load_size(phdr, elf_hdr->e_phnum);
	//计算load_bias,load_bias = elf_hdr + 第一个PT_LOAD节的p_offset - p_vaddr
    linker_so.load_bias = get_elf_exec_load_bias(elf_hdr);
    linker_so.dynamic = nullptr;
    linker_so.phdr = phdr;
    linker_so.phnum = elf_hdr->e_phnum;
	//构建linker的soinfo时,此函数才会标记
    linker_so.set_linker_flag();
  
    // 解析.dynamic的符号表、字符串表、plt、got等到内存中,确定每个表的内存位置,大小和一些其他参数。
    if (!linker_so.prelink_image()) __linker_cannot_link(args.argv[0]);
  
    //重定位
    if (!linker_so.link_image(g_empty_list, g_empty_list, nullptr)) __linker_cannot_link(args.argv[0]);
  
  #if defined(__i386__)
  
    __libc_init_sysinfo(args);
  #endif
  
    // libc中的tls相关
    __libc_init_main_thread(args);
  
    
    //设置最后一个program_header_table节属性
    if (!linker_so.protect_relro()) __linker_cannot_link(args.argv[0]);
  
    // Initialize the linker's static libc's globals
    __libc_init_globals(args);
  
    // store argc/argv/envp to use them for calling constructors
    g_argc = args.argc;
    g_argv = args.argv;
    g_envp = args.envp;
  
    //递归get_childred()函数返回的soinfo列表,然后执行ELF中的init_func_(DT_init段)和init_arrary(DT_init_array段)函数
    linker_so.call_constructors();
  
    //判断正在加载的ELF是不是linker,_start是linker的入口地址,entry_point是正在加载的ELF的地址
    if (reinterpret_cast<ElfW(Addr)>(&_start) == entry_point) {
      async_safe_format_fd(STDOUT_FILENO,
                       "This is %s, the helper program for dynamic executables.\n",
                       args.argv[0]);
      exit(0);
    }
	
	//将ELF加入到 linker_linker_map 双向链表中
	init_linker_info_for_gdb(linker_addr, kLinkerPath);
	
	//拷贝栈上的soinfo到堆中,加入到系统solist中
    sonext = solist = get_libdl_info(kLinkerPath, linker_so, linker_link_map);
    g_default_namespace.add_soinfo(solist);
  
    args.abort_message_ptr = &g_abort_message;
	
	//这个函数是__linker_init()在返回前最后调用的函数,它要创建可执行文件对应的soinfo、调用find_librarys加载依赖库等工作,完成可执行文件运行所需的一系列准备工作。
    ElfW(Addr) start_address = __linker_init_post_relocation(args);
  
    INFO("[ Jumping to _start (%p)... ]", reinterpret_cast<void*>(start_address));
  
    return start_address;
}

linker_so.protect_relro()函数

	//linker_so.protect_relro() 调用 phdr_table_protect_gnu_relro(phdr, phnum, load_bias) 调用 _phdr_table_set_gnu_relro_prot(phdr_table, phdr_count, load_bias, PROT_READ)
static int _phdr_table_set_gnu_relro_prot(const ElfW(Phdr)* phdr_table, size_t phdr_count,ElfW(Addr) load_bias, int prot_flags) {
  
  //phdr:program_header_table在内存中的起始地址
  //phdr_limit:program_header_table节在内存中的结束地址
  const ElfW(Phdr)* phdr = phdr_table;
  const ElfW(Phdr)* phdr_limit = phdr + phdr_count;
  
  //遍历找到program_header_table中的最后一个节
  for (phdr = phdr_table; phdr < phdr_limit; phdr++) {
    if (phdr->p_type != PT_GNU_RELRO) {
      continue;
    }
	
	//设置p_vaddr和p_vaddr + p_memsz的页对齐,计算最后一个节在内存中的起始和结束地址
    ElfW(Addr) seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias;
    ElfW(Addr) seg_page_end   = PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias;

	//使用mprotect函数改变最后一个节权限为只读
    int ret = mprotect(reinterpret_cast<void*>(seg_page_start),
                       seg_page_end - seg_page_start,
                       prot_flags);
    if (ret < 0) {
      return -1;
    }
  }
  return 0;
}

linker_so.call_constructors()函数

	//call_constructors() -> call_array() -> call_function()(init_func_中的函数少了call_array的环节,直接从call_constructors中调用call_function函数执行)
static void call_function(const char* function_name __unused,linker_ctor_function_t function,const char* realpath __unused) {
  if (function == nullptr || reinterpret_cast<uintptr_t>(function) == static_cast<uintptr_t>(-1)) {
    return;
  }

  TRACE("[ Calling c-tor %s @ %p for '%s' ]", function_name, function, realpath);
  //函数调用(此处的Android源码与动态ida调试的调用链不同)
  //ida调用链:call_constructors()函数里面直接就会通过soinfo里面的偏移调用init相关函数,而不是像源码这样经过几个函数最终才会调用,可能是ida反编译的问题吧
  function(g_argc, g_argv, g_envp);
  TRACE("[ Done calling c-tor %s @ %p for '%s' ]", function_name, function, realpath);
}

init_linker_info_for_gdb函数

static link_map linker_link_map;

struct link_map {
  ElfW(Addr) l_addr;
  char* l_name;
  ElfW(Dyn)* l_ld;
  struct link_map* l_next;
  struct link_map* l_prev;
};

static void init_linker_info_for_gdb(ElfW(Addr) linker_base, char* linker_path) {
  
  //初始化linker_link_map双向链表中,正在加载的ELF文件的信息:ELF起始地址 与 ELF的路径名称
  linker_link_map.l_addr = linker_base;
  linker_link_map.l_name = linker_path;

  //参数偏移计算
  ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)*>(linker_base);
  ElfW(Phdr)* phdr = reinterpret_cast<ElfW(Phdr)*>(linker_base + elf_hdr->e_phoff);
  phdr_table_get_dynamic_section(phdr, elf_hdr->e_phnum, linker_base, &linker_link_map.l_ld, nullptr);

}

void phdr_table_get_dynamic_section(const ElfW(Phdr)* phdr_table, //program_header_table 在内存中的起始地址
									size_t phdr_count,            //program_header_table 中节的数量
                                    ElfW(Addr) load_bias,         //program_header_table 在内存中的起始地址
									ElfW(Dyn)** dynamic,          //解析出的动态节的存放位置
                                    ElfW(Word)* dynamic_flags)    //调试时为0,可能是linker的时候
{
  *dynamic = nullptr;
  for (size_t i = 0; i<phdr_count; ++i) {
    const ElfW(Phdr)& phdr = phdr_table[i];
	
	//遍历phdr寻找.dynamic段,ELF动态链接所需的函数符号,重定位等
    if (phdr.p_type == PT_DYNAMIC) {
      *dynamic = reinterpret_cast<ElfW(Dyn)*>(load_bias + phdr.p_vaddr);
      if (dynamic_flags) {
        *dynamic_flags = phdr.p_flags;
      }
      return;
    }
  }
}

/core/java/android/app/ActivityThread.java -> void handleBindApplication(AppBindData data)
//处理 Application 应用绑定方法 , 这是创建 Application 应用核心过程 ;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ActivityManager是Android系统中非常重要的一个组件,负责管理系统中所有Activity的生命周期、进程管理、任务管理等。在本篇文章中,我们将深入了解ActivityManager框架的实现原理。 ## Activity的启动过程 在Android系统中,Activity是应用程序中最基本的界面单元。当用户点击应用程序的图标时,ActivityManager会负责启动应用程序并启动相应的Activity。下面是Activity的启动过程: 1. 应用程序进程启动:ActivityManager会创建一个新的进程来运行应用程序。 2. 应用程序的Application对象创建:在应用程序进程启动之后,ActivityManager会负责创建应用程序的Application对象。 3. Activity对象创建:当用户点击应用程序的图标并选择启动Activity时,ActivityManager会负责创建相应的Activity对象。 4. Activity的onCreate方法调用:ActivityManager会调用Activity对象的onCreate方法,完成Activity的初始化工作。 5. Activity的onStart方法调用:ActivityManager会调用Activity对象的onStart方法,将Activity显示给用户。 6. Activity的onResume方法调用:ActivityManager会调用Activity对象的onResume方法,让Activity成为用户交互的焦点。 7. Activity的生命周期结束:当用户退出Activity时,ActivityManager会负责销毁Activity对象。 ## 进程管理 在Android系统中,每个应用程序都运行在一个独立的进程中。ActivityManager负责管理所有进程,以确保系统的稳定和性能。 当应用程序被启动时,ActivityManager会为其创建一个新的进程并分配一定的内存资源。如果此时系统内存不足,ActivityManager会根据一定的策略来回收一些进程内存,以确保系统正常运行。 ActivityManager还负责监控进程的CPU使用情况和内存使用情况。如果一个进程的CPU使用率过高或者内存使用量过大,ActivityManager会考虑回收该进程的资源,以避免系统崩溃。 ## 任务管理 在Android系统中,任务是指一组相关的Activity。当用户启动一个应用程序时,ActivityManager会创建一个新的任务,并将应用程序的第一个Activity加入到该任务中。 当用户从一个Activity切换到另一个Activity时,ActivityManager会将前一个Activity加入到任务的后台,并将新的Activity显示给用户。当用户点击返回键时,ActivityManager会将当前Activity从任务中移除,并将前一个Activity重新显示给用户。 需要注意的是,当应用程序中的所有Activity都被销毁时,该应用程序的任务也会被销毁。这是因为Android系统中的Activity都是基于任务的,一个应用程序的所有Activity都属于同一个任务。 ## 总结 ActivityManager是Android系统中非常重要的一个组件,它负责管理系统中所有Activity的生命周期、进程管理、任务管理等。在本篇文章中,我们深入了解了ActivityManager的实现原理。熟悉ActivityManager的工作原理对于开发高质量的Android应用程序非常重要。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值