018 Android加固之实现dex加载器
文章目錄
- Android APK加固-安全人員角度
- 關于類加載器
- 類加載器
- 類加載器的種類和個數
- 創建類加載實例
- 類加載器DexClassLoader和PathClassLoader
- 使用類加載器動態加載dex文件
- 制作dex文件
- 動態加載dex文件
- 完整步驟回顧
Android APK加固-安全人員角度
Android安全人員對APK加固采取的角度大概分為以下幾個方面:
那如果繼續再細分一下,實現一個初級的APK加固:
接下來就是從類加載器開始一步步完成APK加固
關于類加載器
類加載器
在JAVA開發中動態加載的技術主要用于加載jar包,使軟件具備動態添加插件的功能,Java代碼都是寫在Class里的,程序運行在虛擬機上時,虛擬機需要把需要的Class加載進來才能創建實例對象并工作,而完成這一加載工作的角色就是ClassLoader。
Android的Dalvik/ART虛擬機如同標準JAVA的JVM虛擬機一樣,在運行程序時首先需要將對應的類加載到內存中。因此我們可以利用這一點,在程序運行時手動加載Class,從而達到代碼動態加載可執行文件的目的。
Android的Dalvik/ART虛擬機雖然與標準Java的JVM虛擬機不一樣,ClassLoader具體的加載細節不一樣,但是工作機制是類似的,也就是說在Android中同樣可以采取類似的動態加載jar或者dex的功能,只是在Android應用中動態加載一個jar或者dex的工作要比Eclipse加載一個插件復雜許多
類加載器的種類和個數
動態加載的基礎是ClassLoader,從名字可以看出ClassLoader就是專門用來處理類加載工作的,所以這個類也叫類加載器,而且一個運行中的APP不僅有一個類加載器。
其實在Android系統啟動的時候會創建一個Boot類型的ClassLoader實例,用于加載一些系統Framework層級需要的類,我們的Android應用里也需要用到一些系統的類,所以APP啟動的時候也會把Boot類型的ClassLoader傳進來。此外,APP也有自己的類。
這些類保存在APK的dex文件里面,所以APP啟動的時候,也會創建一個自己的ClassLoader實例,用于加載自己dex文件中的類。下面我們在項目里驗證看看。
public void enmuClassLoader(){int count=0;//獲取當前包的ClassLoaderClassLoader classLoader=getClassLoader();if (classLoader!=null){Log.d("GuiShou","[onCreate] classLoader"+count++ +":"+classLoader.toString());}while (classLoader.getParent()!=null){classLoader=classLoader.getParent();Log.d("GuiShou","[onCreate] classLoader"+count++ +":"+classLoader.toString());}}可以看見有兩個ClassLoader實例,一個是BootClassLoader,系統啟動時候創建的,一個是PathClassLoader,應用啟動時創建的。由此也可以看出,一個運行的Android應用至少有兩個ClassLoader。
創建類加載實例
動態加載外部Dex文件的時候,我們也可以使用自己創建的ClassLoader實例來加載dex里面的class,不過ClassLoader的創建方式有點特殊,我們先來看看它的構造方法
private ClassLoader(Void unused,ClassLoader parent){this.parent=parent; }創建一個ClassLoader的時候,需要使用一個現有的ClassLoader的實例作為新創建的實例的Parent。這樣一來,一個Android應用,甚至整個Android系統里所有的ClassLoader實例都會被一棵樹關聯起來,這也是ClassLoader的雙親代理模型(Parent-Delegation Model)的特點
類加載器DexClassLoader和PathClassLoader
在Android中,ClassLoader是一個抽象類,在實際開發過程中,我們一般是使用其子類DexClassLoader和PathClassLoader來加載類的。
它們的不同之處是:
- DexClassLoader可以加載jar/dex/apk,可以從SD卡中加載未安裝的APK
- PathClassLoader只能加載系統中已經安裝過的APK
根據功能,我們更加傾向于使用DexClassLoader,所以接下來先使用DexClassLoader來加載一個簡單的dex文件
使用類加載器動態加載dex文件
制作dex文件
為了能與Android的類有互動,先寫這樣一段代碼
public class TestActivity {public void CreateVew(Activity ac){//創建一個TextViewTextView tv=new TextView(ac);//創建布局 設置參數FrameLayout.LayoutParams params=new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT,FrameLayout.LayoutParams.WRAP_CONTENT);//設置控件到頂端的距離params.topMargin=0;//設置控件的位置params.gravity= Gravity.TOP|Gravity.CENTER_HORIZONTAL;tv.setText("TestActivity View");//添加TextView到Avtivity中ac.addContentView(tv,params);} }這段代碼的效果就是顯示一段TextView到Activity中
這次代碼直接在Android項目中編譯,然后在app的目錄中查找class文件復制到自己的目錄中即可。類所在的目錄是app/build/intermediates/classes/debug/包名
接下來把當前的這個Activity進行反編譯得到的smali文件轉成一個dex文件放到這個工程的資源文件里
將apk用AndroidKiller打開,分析完成后打開smali文件路徑,復制出一份副本
刪除其余的smali文件,只保留我們自己寫的TestActivity類的smali,然后通過smali.jar將其轉成dex文件
然后在main文件夾下新建一個assets文件夾,將dex文件復制到里面,并且刪除TestActivity類
動態加載dex文件
現在我們已經有了一個制作好的dex文件,接著就需要將這個dex文件動態加載進來。想要動態加載dex,需要下面幾個步驟。
首先拷貝dex文件
public String CopyDex(String dexName){//獲取Asserts目錄管理器AssetManager as=getAssets();//合成路徑 data/data/包名/files/dex名稱String path=getFilesDir()+ File.separator+dexName;Log.d("GuiShou",path);//創建文件流try {FileOutputStream out=new FileOutputStream(path);//打開文件InputStream is=as.open(dexName);//循環讀取文件,拷貝到目標路徑byte[] buffer=new byte[1024];int len=0;while ((len=is.read(buffer))!=-1){out.write(buffer,0,len);}//關閉文件out.close();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return path;}接著創建DexClassLoader
public DexClassLoader GetLoader(String path){//創建dex的類加載器,返回DexClassLoader對象DexClassLoader dexClassLoader=new DexClassLoader(path, //dex文件路徑getCacheDir().toString(), //優化后的dex文件路徑null, //原生庫路徑getClassLoader() //父類加載器);return dexClassLoader;}最后調用加載的dex中的class方法
public void execClassMethod(DexClassLoader dex,String ClassName, String MethodName){try {//獲取加載的類信息Class TestDex=dex.loadClass(ClassName);//獲取構造方法 無參構造Constructor localConstructor=TestDex.getConstructor(new Class[]{});//調用構造方法 創建對象Object instance=localConstructor.newInstance(new Object[]{});//獲取成員方法Method methodTest=TestDex.getDeclaredMethod(MethodName,new Class[]{Activity.class});//取消java訪問檢查,提高反射速度methodTest.setAccessible(true);//調用方法 this指針傳進去methodTest.invoke(instance,new Object[]{this});} catch (ClassNotFoundException e) {e.printStackTrace();} catch (NoSuchMethodException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}}最后調用以上三個函數
//加載dexpublic void loadDex(){//拷貝自定義資源中的dex到程序目錄下String dexPath=CopyDex("test.dex");//創建一個DexClassLoader,加載dexDexClassLoader dexClassLoader=GetLoader(dexPath);//調用加載dex中的class方法execClassMethod(dexClassLoader,"com.example.classloaderdemo.TestActivity","CreateVew");}到這里,代碼就已經全部完成了。
接著運行程序,
效果和之前寫的dex文件代碼一致,那么就說明我們已經完成了動態加載dex文件的過程。
完整步驟回顧
總結
以上是生活随笔為你收集整理的018 Android加固之实现dex加载器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 017 Android加固之APK混淆和
- 下一篇: 020 Android之so文件动态调试