本文對dex加殼機制中的DexClassLoader部分做了稍微詳細的介紹,解決了我的一些疑問,收藏之!
原文位址:http://blog.csdn.net/beyond296089727/article/details/45418803
JAVA 運作所需要依賴的API的是動态連結的,這個跟C/C++不一樣,C/C++開發的程式可以是靜态連結的也可以是動态連結的,其中靜态連結是在編譯時已經做好的,
動态連結的運作時才連結的,也就是window 的DLL檔案,linxu的SO檔案都是動态連結庫。java是動态連結,也就是說我們代碼中 new一個對象,
static Class 調用,Class.forNmae(),Class.loderclass()這些動作,JVM先把這個class檔案
加載到記憶體然後産生一個對象并吧這對象的位址傳回給new,這樣我們就可以調用對象的成員了。
其中JVM把class檔案加載到記憶體就是用classloader了,關于classloader的詳解可以參考這裡http://blog.chinaunix.net/uid-21227800-id-65885.html
很高興的是JAVA API中為我們提供了ClassLoader這個類,我們用這個類可以通過反射加載自己的class,正因為如初使得JAVA有很強的生命力,在防破解
加殼自定義插件方面發揮的很好,比如ecplise的思想就是“一切都是插件”,他的功能都是通過安裝插件完成的,擴充性很強,是很多公司都為他開發插件作為IDE
,也很受開發者歡迎,其中ecplise就是靠ClassLoader加載插件了,這就看出了ClassLoader的強大。
在android中雖然android 的APK是用JAVA開發的,但是android 的虛拟機dalvik并不是java标準的JVM,android的可執行檔案是DEX,而JVM的是.class他們并不相容,
關于他們的差別可以參考這裡http://www.w2bc.com/Article/14646 。自然的用JAVA 标準的classloader 不能加載DEX,但是谷歌還是基本支援全部JAVA的特性,
是以classloder這個很好的JAVA特性谷歌不會放過,是以谷歌也提供給我們加載DEX的classloader,他的特性和用法跟JAVA标準的classloader差不多。
一個是DexClassLoader另一個是PathClassLoader,他們的差別可以參考這裡http://www.apkbus.com/forum.php?mod=viewthread&tid=138499
這裡隻介紹DexClassLoader,那我們怎樣用她來加殼免殺?
先看看API文檔的說明他有四個參數,dexpath表示所要加載DEX檔案的路徑,
optimizedDirectory便是優化的DEX存放路徑,
libraryPath表示DEX中如果調用自己的SO檔案的路徑,parent表示為自己的DexClassLoader指定父classloader
我們知道Classloader是雙親委派機制的,這是因為每個classloader隻會在指定的檔案了尋找和加載class,比如 上面如果在dexpath參數裡指定了/sdcard/test.dex,那麼這個dexclassloader隻會在/sdcard/test.dex查找class, 如果我們的DEXclassloader通過反射加載了/sdcard/test.dex裡的一個class com.my.test,而com.my.test如果 有 這樣一句 String str="22555",我們知道JAVA是動态連接配接語言,這是如果 String類需要加載,那麼他預設的classloader也是所屬class的classloader,比如這裡所屬類是com.my.test,而com.my.test的classloader是我們的 DExclassloader,而如果我們的DExclassloader去自己的檔案裡test.dex找String類自然找不到,就抛出ClassNotFoundExcpiton,是以雙親委派機制就解決這個問題,當一個classloaer去加載一個class時他先調用他的parent Classloader去加載,如果parent Classloader沒有在自己的檔案裡找到這個classs那麼子classloader才去加載。
那麼怎麼加殼和免殺?
其實思路很簡單,把我們所要加殼保護的代碼導出成DEX,關于導出DEX看着裡 http://www.cnblogs.com/over140/archive/2011/11/23/2259367.html 然後加密,放到伺服器或者APK其他檔案夾, 運作時通過再讀取檔案解密然後再用dexclassloader加載運作,這樣就可以保護我們的代碼的, 其實愛加密,360等加強都是這樣的基本思路
比如愛加密的就這這樣
箭頭指向的就是原DEX,就是被加密保護的,至于加密解密其實很簡單,我舉個最簡單的例子,把要保護的DEX檔案
的每個位元組值+1,解密時-1,這樣即使拿到DEX也沒用。加密我們可以事先通過算法加密,然後才放到APK裡,
關鍵是解密,我們都知道APK的入口是application類,這時我們這樣在applicatian類裡運作加密程式完成DEx的解殼然後用dexclassloader加載并運作,
當然了處于安全,解殼和加載DEx的算法應該放在SO,在application裡通過JNI調用,其實愛加密也是這樣做
圖中的箭頭指向就是他的解密SO
關于愛加密詳細原理可以參考這裡
http://blog.csdn.net/chengyingzhilian/article/details/38372601
是以用dexclassloader加殼就這麼簡單
至于包名的跟不用說了,每個APK都有自己唯一的包名,沒有源碼情況下
改包名麻煩,也就是說包名一般不該,是以給防毒軟體提供了很好的特征
依據,也就是說這個包名的APK如果被檢測為木馬的,哪怕代碼在修改
包名不改很容易看出。
不過這裡我不提倡大家用于實質非法活動,這是供大家學習研究,對殺毒
安全的研究和了解
關于用dexclassloader動态加載DEx存在的問題,我們知道dexclassloader
加載的類生命周期是虛拟機直接管理,但是對于元件類比如activity他們的聲明周期不是由虛拟機直接管理,而是由各種對應的架構層manager管理,比如activity是有activitymanger
管理這就是為什麼我們不能new activity的原因,隻能通過startActivity()反射加載。
但是最終activity也是使用者classloader加載的。之是以這個元件類是用各架構層管理器
管理是有原因的,因為這些類的聲明周期是随機的,不可預知的,比如接受短信的
BroadcastRecivier,當有短信來時系統收到了,交給廣播管理器廣播intent,如果我們的
的androimainfest.xml定義了一個<recivier>,來接受短信,這是廣播管理器會通知我們APP的主線程new 我們的BroadcastRecivier類并且調用onReciver()函數,這一切都是通過反射加載的。也就是new BroadcastRecivier是有短信來了才NEW,而 短信是随機的,是以我們不可能在代碼中new ,你知道短信仕麼時候來嗎?!那麼你NEW?!
這裡就關鍵了,BroadcastRecivier元件類最終也是用classloader加載的,如果這個classloader是我們定義的Dexclassloader那麼不管我們的元件類放在哪裡都會被正常加載并且不存在生命周期問題,然而加載這些元件類的都是都APK的主線程ActivityThread來做,他用的是他自己的classloader加載,我們隻把他的classloader替換成我們移動一的就得了
關于實作方案我是參考jack_jia的來做http://blog.csdn.net/androidsecurity/article/details/8809542
紅色箭頭是包含有activity等元件類的DEX,橢圓橢圓圈是替換classloaderder