文章目錄
- Android APK加固-安全人員角度
- 關于類加載器
- 類加載器
- 類加載器的種類和個數
- 創建類加載實體
- 類加載器DexClassLoader和PathClassLoader
- 使用類加載器動態加載dex檔案
- 制作dex檔案
- 動態加載dex檔案
- 完整步驟回顧
Android APK加固-安全人員角度
Android安全人員對APK加固采取的角度大概分為以下幾個方面:
- 將可執行代碼dex檔案加密,能夠動態解密并執行
- 能夠檢測當前狀態是被除錯,想盡一切辦法反除錯
那如果繼續再細分一下,實作一個初級的APK加固:
- 使用類加載器動態加載dex檔案
- 設計傀儡dex替換原dex,動態加載dex
- 在傀儡dex中的各種地方增加反除錯代碼
接下來就是從類加載器開始一步步完成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;
//獲取當前包的ClassLoader
ClassLoader 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){
//創建一個TextView
TextView 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檔案從自定義資源檔案夾拷貝到程式目錄下
- 創建一個DexClassLoader,加載dex
- 呼叫加載的dex中的class方法
首先拷貝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();
}
}
最后呼叫以上三個函式
//加載dex
public void loadDex(){
//拷貝自定義資源中的dex到程式目錄下
String dexPath=CopyDex("test.dex");
//創建一個DexClassLoader,加載dex
DexClassLoader dexClassLoader=GetLoader(dexPath);
//呼叫加載dex中的class方法
execClassMethod(dexClassLoader,"com.example.classloaderdemo.TestActivity","CreateVew");
}
到這里,代碼就已經全部完成了,
接著運行程式,

效果和之前寫的dex檔案代碼一致,那么就說明我們已經完成了動態加載dex檔案的程序,
完整步驟回顧
- 新建一個工程,創建TestActivity類,實作一個創建view的方法
- 編譯生成apk,使用AndroidKiller反編譯,將除了TestActivity.smali代碼外的全部smali檔案洗掉,編譯生成dex檔案
- 在專案main檔案夾創建asserts檔案夾,將生成的dex拷貝到這個目錄下
- 在代碼中完成以下步驟
- 拷貝自定義資源中的dex到程式目錄下
- 創建一個dexClassLoader加載dex
- 呼叫加載dex中的class方法
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/292759.html
標籤:其他
下一篇:Android FFmpeg集成
