Android APP加固與脫殼基礎 ---- Android App應用啓動時、類加載器的初始化過程

如果學習Android加固與脫殼不學習這些基礎的話,那麼後面加固點和脫殼點能讓你輕輕鬆鬆看得雲裏霧裏,不知所以。想學這一門手藝,該跨的門檻一點兒馬虎不得,但是我有預感,當你把這些基礎底層難啃的知識整出些門道之後,看後面的加固和脫殼出現的名詞和爲什麼這樣做時,你就會感到:原來是這樣。

當我們在手機屏幕上點擊一個APP之後,從技術角度透過屏幕和硬件,觀察它的內部發生了哪些變化,在安卓系統又經過了怎樣的流程呢?

一、啓動的地方

在我們編寫Android應用的時候,邏輯開始的地方是onCreate(),但是當我們點擊APP的時候,首先是經過Android系統出發,這裏是真正啓動的代碼:

// 這裏是真正啓動的代碼:
Process.ProcessStartResult startResult = Process.start(
 entryPoint, 
 app.processName,
 uid, uid, gids, 
 debugFlags, 
 mountExternal, 
 app.info.targetSdkVersion, 
 app.info.seinfo, 
 requiredAbi, instructionSet, 
 app.info.dataDir, entryPointArgs
);

Process.start裏面有很多參數,現在不需要疑惑它們是幹啥的,隨着後面的介紹,你會發現好多參數並不在這個課題的討論之下,可以不用管它。

但是這個Process.start()函數很重要
app.processName是進程名,在安卓系統中,經歷了這個過程:
圖1
Process.start()通過Binder IPC創建系統服務(system_server),然後系統服務通過socket發送請求給Zygote這塊流程,就和安卓系統啓動虛擬器流程圖對上了。
圖2
我們看下圖2中的system_server主要乾了哪些事。
看精簡版的部分源代碼:

ZygoteInit.java

private static boolean startSystemServer(String abiList, String socketName) throws MethodAndArgsCaller,RuntimeException{
  ...
    //Request to fork the system server process
    pid = Zygote.forkSystemServer(
  	...
  	);
  ...
    
    //子進程返回0,即systemServer
    if (pid == 0){
      if (hasSecondZygote(abiList)){
        waitForSecondaryZygote(socketName);
      }
      handleSystemServerProcess(parsedArgs);
    }
}
  • 0:代表着是在新建的子進程中執行的。
  • pid of child:在Zygote創建的父進程裏再創建的子進程
  • -1:代表出錯。

這裏我們分析下pid = 0的邏輯,我的理解是APP運行時,不會是由Zygote創建的父進程直接創建子進程給APP,因爲在我們打開應用程序之前,Zygote已經創建了多個系統自帶的應用程序。

if (pid == 0)後的邏輯。

在start()方法通過socket的方式向Zygote進程發起創建請求之後,Zygote.forkSystemServer()會調用handleSystemServerProcess()

private static void handleSystemServerProcess(ZygoteConnection.Arguments parsedArgs)throws ZygoteInit.MethodAndArgsCaller{
  //關閉socket服務
  closeServerSocket();
  ...
  ...
  //創建classLoader並且把它設爲currentThread()當前線程的classLoader
    ClassLoader cl = null;
  if (systemServerClasspath != null){
    
    // createSystemServerClassLoader()創建的是一個PathClassLoader
    cl = createSystemServerClassLoader(systemServerClasspath, parsedArgs.targetSdkVersion);
    
    Thread.currentThread().setContextClassLoader(cl);
  }
  ...
}

運行到這裏時,代表當前是系統服務的進程,所以也創建了系統服務的classLoader,設置了Thread.current().setContextClassLoader()類加載器,後面的應用中所有的類默認是通過這個類加載器來加載的。

至於什麼是classLoader和類加載器的概念,可以閱讀這篇文章

二、創建Application

Application是APP運行的基礎,每當應用程序啓動時,系統會自動將這個類進行初始化,Application的聲明週期就是整個APP的生命週期。

在擁有了Thread.current().setContextClassLoader()類加載器後,通過socket反射調用了ActivityThread.main()方法,創建了ActivityThread後,進行attach()操作。

ActivityThread.java

public static void main(String[] args){
  ...
  Looper.prepareMainLooper();
  ActivityThread thread = new ActivityThread();
  thread.attach(false);
  ...
    
  Looper.loop()
  throw new RuntimeException("Main thread loop unexpectedly exited");
}

// 在thread.attach()裏面進行了attachApplication(mAppThread)同時會調用thread.bindApplication() 這裏是ActivityThread.bindApplication()

public final void bindApplication(...){
  ...
  //最後一句,發送了一條message消息
  sendMessage(H.BIND_APPLICATION, data);
}

//在ActivityThread.handleMessage()中對BIND_APPLICATION的接收後調用了handleBindApplication()
// 真正綁定application的地方
private void handleBindApplication(AppBindData data){
  ...
  ...
  ...
  Application app = data.info.makeApplication(data.restrictedBackupMode, null);
  mInitiApplication = app;
  ...
}

言而總之,attach操作,是把創建的系統服務進程和我們的app進行了一系列的綁定,不做過多分析,我們只看重要的部分。

attach操作接下來會調用LoadedApk.java, ApplicationLoaders.java部分

這兩個類我們從名字上就能看出一二,是幹什麼的。
其中LoadedApk.java裏的內容是我們脫殼和加固內容中非常重要的類,後續遇到時再講。

其中,LoadedApk.makeApplication()裏面的代碼會去獲取classLoader,並且創建appContext,在通過classLoader和appContext去創建application對象。

// LoadedApk.java
public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation){
  ...
  // getClassLoader()是去獲取一個與當前apk相關聯的pathClassLoader,
  java.lang.ClassLoader cl = getClassLoader();
  ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
  
  //app爲application
  app = mActivityThread.mInstrumentation.newApplicationn(
  cl, appClass, appContext);
}

總結:創建appication的過程

  • mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext)
  • 第一個參數cl,是當前app的classLoader
  • 第二個參數是appClass,當前app application的名字,檢索的包名
  • appContext是新建的context
  • newApplication,利用cl.loadClass(appClass)得到當前app的application
  • 補充loadClass後得到的clazz,下一步進行

其中app.attach(context),會調用application的attachBaseContext();

然後調用instrumentation.callApplicationOnCreate(app),會調用application.onCreate()方法;

目前爲止,thread.attach();引起了一系列的事件,現在已經正式創建了appication,可以執行application的onCreate()方法。

三、apk什麼時候與classLoader什麼時候關聯

我們看下創建Application的時候這個語句:

// getClassLoader()是去獲取一個與當前apk相關聯的pathClassLoader,
  java.lang.ClassLoader cl = getClassLoader();

這裏稍微提一下,上面的代碼出現的pathClassLoader,是動態加載部分的內容,順便提一下它的作用和同類。

  • DexClassLoader:可以加載jar/apk/dex,可以加載sd卡中未安裝的apk;
  • PathClassLoader:只能加載系統中已經安裝過的 apk;

實現cl的getClassLoader實現:

public ClassLoader getClassLoader() {
  synchronized(this){
    if (mClassLoader == null){
      createOrUpdateClassLoaderLocked(null)
    }
    return mClassLoader;
  }
}

private void createOrUpdateClassLoaderLocked(List<String> addedPaths){
  ...
  //
  mClassLoader = ApplicationLoaders.getDefault().getClassLoader("", mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath, libraryPermittedPath, mBaseClassLoader);
}

對mClassLoader進行了賦值,那內部的ApplicationLoaders.getDefault().getClassLoader()是如何實現的呢?

對於ApplicationLoaders這個類,它裏面維護了一個mLoaders,map結構,key爲string,可以理解爲 包名,value爲ClassLoader(類加載器)。

public ClassLoader getClassLoader(String zip, ...){
  ...
  ClassLoader loader = mloaders.get(zip);
  if(loader != null){
    return loader;
  }
  PathClassLoader pathClassloader = PathClassLoaderFactory.createClassLoader(...);
  ...
  mLoaders.put(zip, pathClassloader);
  return pathClassloader;
  ...
}

每個app進程都有唯一的ApplicationLoaders實例,後續則通過apk的路徑(zip參數)查詢返回classLoader。

因爲ApplicationLoaders裏維護了classLoader的一個map常量mLoaders,所以一個進程可以對應多個APK。

這樣說吧,一個app有唯一的ApplicationLoaders實例對應,但是一個進程裏的ApplicationLoaders,可以維護多個apk。

總結一下,APP啓動時發生的事情:

  • 先從zygote fork一個進程
  • fork進程之後,會調用ActivityThread.main(),new出一個ActivityThread對象並且會把它與application關聯起來,即attach();
  • attach會引發一系列事件,先後創建classLoader, appContext和application把application和activityThread關聯起來。
  • 調用instrumentation.callApplicationOnCreate(app),觸發application的onCreate()方法。

寫到這裏,我不禁留下了熱淚,過程如此之多,差點把自己繞進去了。不過消化這個過程是必須的,我用了一張比較詳細的思維導圖來描述這整個過程。

腦圖

這裏提示下,光看沒有用,最好動手對照着畫下自己理解的思維導圖,等你把這些東西消化後理解流程就水到渠成了。

Refer:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章