首先要非常感謝大神提供了這么好的一篇文章,幫助我解決了1年我都沒能解決的問題。其次感謝這兩天微博好友給我的留言與評論。
閱讀本文之前請先閱讀大神的這篇。
http://www.luzexi.com/unity3d-%E9%87%8D%E6%96%B0%E7%BC%96%E8%AF%91mono%E5%8A%A0%E5%AF%86dll/
我做的時候有些細節不太一樣。一樣的地方我就不寫了,我把不一樣的地方寫出來。
加密DLL首先要找準unity版本對應的mono,地址在這里 https://github.com/Unity-Technologies/mono
這里有個很惡心的事情,unity的mono版本并不是按小版本分的,比如我想找unity4.6.1 對應的mono那么它就沒有,unity只提供4.3x 或者 4.6x 或者5.1x 這種大版本的mono .從提交時間上來看更新的很隨意啊。我感覺要想找到對應的unity版本,可以根據unity這個版本發布的時候,然后在github上找對應時間的mono版本。。
如下圖所示,打開網頁后,找到對應的branches版本, 這里選擇unity-4.6 或者 unity-5.1 這兩個版本我已經測試通過。別的版本希望大家都能來參與測試。
在說說說惡心的地方,我下載4.6以后,把mono編譯出來。放在unity4.6.1的打出來的包里 死活會報錯, 但是4.6.6就沒問題了。。 不過還好我這里兩個項目 一個是用unity4.6.6還有一個是unity5.1.1目前都沒出現問題。
下面我都用unity4.6舉例,其他版本原理都一樣我就不贅述了。
1.github下載下來對應的mono解壓放在本地,在終端里先cd到這個目錄下。
2.把打包腳本拖入終端中(注意腳本的路徑),然后就開始耐心等待吧。估計5分鐘左右就OK了。
3.打包腳本分兩種, 一個是 arm的,還有一個是x86,執行build_runtime_android.sh 就可以了, 它會自動調用
build_runtime_android_x86.sh。
打包腳本我們需要改一下,因為下載下來的腳本直接運行打的是debug版本,效果就是打出來的.so比unity自帶的大很多。我們要改成release版本。
如下圖所示,左邊是x86,右邊是arm。把CFLAGS里的-g改成-O2 (O0 ,O1,O2,OS,O3分了好幾個壓縮檔次,我覺得O2就可以了)然后在LDFLAGS里加上-Wl,–gc-sections \ 就行了。
注意:今天同事說x86下有些手機進游戲卡死。后來經過一番分析,原來是x86的編譯選項和arm不一樣。如下圖所示,在X86.sh 這里只把-g去掉就行。。別的什么都別改。切記切記!!!
然后在下面把這兩句代碼注釋掉,不然編譯的時間就要增加了。
clean_build “$CCFLAGS_ARMv5_CPU” “$LDFLAGS_ARMv5” “$OUTDIR/armv5”#clean_build “$CCFLAGS_ARMv6_VFP” “$LDFLAGS_ARMv5” “$OUTDIR/armv6_vfp”
在打mono.so前記得改一下解密算法。因為在測試所以解密和加密算法我們就寫簡單一點。如下圖所示,mono/metadata/image.c里面找到 mono_image_open_from_data_width_name 。 因為我只會對自己寫的c#編譯后的dll加密,所以這里判斷一下是否是我們自己的dll,解密算法很簡單就是讓字節下標為1的字節-1。
如果你要熱更DLL時一定要注意!!這里一定要先判斷一下name是否為NULL 不然使用System.Reflection.Assembly.Load 在Android平臺反射調用DLL的時候unity 會掛的。
1
2
3
4
5
6
if(name != NULL)
{
if(strstr(name,"Assembly-CSharp.dll")){
data[0]-=1
}
}
還有如果想在 mono里打印Log的話可以使用
include <glib.h>
g_message(“momo: %s”,str);
OK 然后開始編譯mono吧。arm 和x86 兩個大概 5 分鐘左右就能編譯完成。對應會會放在mono根目錄build的文件夾里。然后回到生成的adnroid工程中,把libmono.so 分別放在x86和armeabi-v7a文件夾下。因為我項目用了slua所以這里也會有一些第三方的.so
再說說自動化的問題,DLL每次代碼變更都會重新生成一個新的,那么我總不能每次都手動加密DLL然后在手動的拷貝到assets下面吧。。
再說一句,我的項目在處理自動化打包時用的是adnroid的ant打包。也就是先把unity導出成一個android 工程。然后在打包。所以我的自動化就可以是當android工程生成后,然后把dll讀取到內存里,加密后在重新寫到原來工程的位置上。如果有朋友不太懂自動化,可以在我博客里搜索一下,以前我有寫過。
http://www.xuanyusong.com/archives/3384 環境變量如果你不會加的話,也可以看我這篇文章。
這段代碼的意思就是當eclipse的android工程生成后,緊接著就給dll加密。。字節一變那么Dll其實就變成了一個普通的二進制文件。這樣用各種反編譯Dll的工具就都打不開了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//shell腳本來自動調用unity中的這個c#方法
static void ExportAndroidProject()
{
List<string> args = GetArgs("ExportAndroidProject");
if (string.IsNullOrEmpty (BuildPipeline.BuildPlayer (GetBuildScenes (), args [0], BuildTarget.Android, BuildOptions.AcceptExternalModificationsToPlayer)))
{
encryptDll (args [0]);
}
}
static private void encryptDll(string path)
{
//DLL在android工程中對應的位置
string inpath = path +"/"+ PlayerSettings.productName +"/assets/bin/Data/Managed/Assembly-CSharp.dll";
if(File.Exists(inpath)){
//先讀取沒有加密的dll
byte[] bytes = File.ReadAllBytes (inpath);
//字節偏移 DLL就加密了。
bytes [0] += 1;
//在寫到原本的位置上
File.WriteAllBytes (inpath, bytes);
}
}
然后還有前面我們編譯出來的兩個 mono.so 也要在這里自動化一并拷貝到這個工程對應的目錄下面(可以在shell里拷貝,也可以在C#里拷貝)。 接下來就調用自動打包apk就行了。。總之最后的效果就是Dll不能被解開了。如下圖所示。
但是,高興的別太早。DLL是解不開了,但是你的解密算法是寫在.so里面的,那么對方反編譯你的.so取出解密算法,隨便寫個小工具就可以把你的DLL逆向回來。。
在windows上下載ida pro 神器(真是道高一尺魔高一丈啊)。
http://www.h4ck.org.cn/2014/08/ida-pro-6-5-with-hex-rays-x86-decompiler-v1-5-and-hex-rays-arm-decompiler-1-7/
然后打開我們編譯的libmono.so
找到mono_image_open_from_data_width_name 方法,然后點擊F5 解密算法就破解了。(下面我找到了一個避免破解的方法,在本文的最后)
怎樣才能避免別人這么容易破解你的DLL呢?請看我的下一篇文章 Unity3D研究院之Android二次加密.so二次加密DLL(八十二)
本文固定鏈接: http://www.xuanyusong.com/archives/3553
轉載請注明: 雨松MOMO 2015年07月02日
于 雨松MOMO程序研究院 發表