編輯:關於android開發
【版權所有,轉載請注明出處。出處:http://www.cnblogs.com/joey-hua/p/5402599.html 】
此方案的目的是隱藏源碼防止直接性的反編譯查看源碼,原理是加密編譯好的最終源碼文件(dex),然後在一個新項目中用新項目的application啟動來解密原項目代碼並加載到內存中,然後再把當前進程替換為解密後的代碼,art模式下也沒問題。好了,廢話不多說,來看代碼,下面是最終想運行的項目,也稱為原項目:
這是原項目的目錄,項目名叫Hello,就兩個activity,第一個activity的布局如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="我是Hello項目的FirstActivity" android:textSize="20sp" /> <Button android:id="@+id/btn" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="跳轉" /> </LinearLayout>
非常簡單,沒什麼好說的,運行效果如下:
接下來介紹dex加密工具,名字叫DexJiami,關鍵代碼如下:
private static final String root = "C:"; private static final String path = root + "/Users/lenovo/Documents/mygithub/android-protection/LoadDex/dexFileTmp"; private static final String path_encrypt_source = path + "/mycode.jar"; private static final String path_encrypt = path+"/mycode.dat"; private static String cmd_dex2jar = "cmd /c "+root+" && cd "+path+ " && jar cvf " + path_encrypt_source + " classes.dex"; private static String key="GiEhjghmZIO7RTWyycQ9PQ=="; /* * 需要生成新的秘鑰時使用 */ // static { // try { // key = AESUtils.getSecretKey(); // } catch (Exception e) { // e.printStackTrace(); // } // }
這個path就是你准備把dex放置的一個臨時文件夾,生成加密文件用;key的值就是加密和解密用的秘鑰,咱們用的AES對稱加解密,這裡的key值是通過這個靜態塊來生成的,生成一次就行了,直接copy到引號,如果想生成新的秘鑰就去掉注釋行。
public static void main(String[] args) throws Exception { dex2Jar(); //給dex打成jar包 encrypt(); //對jar加密並生成為.dat文件 }
如上,這是從我公司項目的自動化打包工具上裁剪了的,現在整個工具就兩個方法,第一個是把原項目的源碼dex文件打成jar包,然後再對jar包用key值加密並生成dat文件。
加密工具使用方法,先從原項目的/bin目錄下找到classes.dex,這個文件就是原項目所有的java源碼生成的最終的字節碼文件,android系統運行你寫的程序就是加載這個dex文件。把它拷貝到你自定義的dexFileTmp文件夾,然後run這個加密工具,最終生成的文件如下:
最終要用的就是mycode.dat這個文件,怎麼用待會再說,得先介紹第二個android項目DexLoader,這裡稱為啟動項目,先介紹java版的,後面再介紹jni版,先看下java版的目錄結構:
這裡注意,除了上面的這幾個java文件不一樣之外,所有的資源文件和AndroidManifest都是直接從原項目拷貝過來的,還有就是把上面的mycode.dat文件拷貝至啟動項目的assets目錄下,下面的關鍵代碼為首先在進程的系統文件夾下面創建一個自定義的app_cache文件夾,作為加密文件的中轉文件夾,然後解密dat文件並變成jar,變成jar包後系統才能識別並把classes.dex抽取出來。
File odex = this.getDir("cache", MODE_PRIVATE); String odexPath = odex.getAbsolutePath(); System.out.println("odexPath--->" + odexPath); try { Util.decryptFile(this, odexPath+"/mycode.jar"); } catch (Exception e2) { // TODO Auto-generated catch block e2.printStackTrace(); }
接下來的關鍵代碼如下,把解密後的jar包,也就是原項目的代碼加載至內存:
DexClassLoader dLoader = new DexClassLoader(odexPath+"/mycode.jar", odexPath, "/data/data/" + base.getPackageName() + "/lib/", base.getClassLoader().getParent());
接下來程序就會走onCreate方法,大量用到了RefInvoke類,這是一個專門調用反射的類,目的是獲取系統的private變量和執行private方法,就不多介紹了,最終替換當前進程的代碼為剛才加載至內存的代碼,最終程序運行如下:
而且還可以跳轉至第二個activity。大功告成!
下面介紹一下Jni版,先看下目錄結構
對比java版可以看出,解密的代碼不見了,其實是被封裝到了jni層,下面是java層的關鍵代碼:
static{ System.loadLibrary("load"); } @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); Object currentActivityThread = RefInvoke.invokeStaticMethod( "android.app.ActivityThread", "currentActivityThread", new Class[] {}, new Object[] {}); String packageName = getPackageName(); Map mPackages = (Map) RefInvoke.getFieldOjbect( "android.app.ActivityThread", currentActivityThread, "mPackages"); WeakReference wr = (WeakReference) mPackages.get(packageName); run(this, base, "/data/data/"+ base.getPackageName() + "/lib/", base.getClassLoader().getParent(),wr.get()); } private native String run(Application wrapper, Context context, String libPath,ClassLoader classLoader, Object obj);
可以對比java版的代碼,attachBaseContext裡的部分關鍵代碼都封裝在run函數裡,目的就是隱藏解密算法。jni層的代碼就不多介紹了,代碼原理本身和java版是一樣的,只不過多了在jni層獲取java層的對象和方法的過程。後續工作還需要對so文件加殼及反調試,本版本中並沒有,當然本版本也有一定的安全作用,畢竟可以防止反編譯apk直接查看源碼。運行結果和上面一致,也可以跳轉到第二個activity。
最後我們再來看下愛加密處理後的源碼,將愛加密處理後的apk解壓,對classes.dex執行反編譯查看源碼,以及assets目錄下的ijiami.dat,如下圖:
分析而知愛加密也是運用了動態解密加載dex文件並替換當前進程的方式實現了隱藏原項目源碼的行為。
【加密工具】
【原項目】
【java版啟動項目】
【jni版啟動項目】
多選按鈕(CheckBox),多選按鈕checkbox今天我們介紹的是Checkbox多選框: 1.Activity //復選框,[基礎控件]---狀態切換控件Com
android:Activity數據傳遞之對象(parcelable) 這篇文章裡面寫了用seralizable使對象序列化在Activity直之間進行傳遞 sera
導入arr包,導入arr提起項目的aar包 導入目標項目中 添加依賴
在Ubuntu Server14.04上編譯Android6.0源碼,ubuntu編譯安卓源碼 此前編譯過Android4.4的源碼,但是現在Android都到了7.0