首先要非常感謝大神提供了這么好的一篇文章膛堤,幫助我解決了1年我都沒能解決的問題躬柬。其次感謝這兩天微博好友給我的留言與評論原杂。
閱讀本文之前請先閱讀大神的這篇链蕊。
http://www.luzexi.com/unity3d-%E9%87%8D%E6%96%B0%E7%BC%96%E8%AF%91mono%E5%8A%A0%E5%AF%86dll/
我做的時候有些細(xì)節(jié)不太一樣敦锌。一樣的地方我就不寫了馒疹,我把不一樣的地方寫出來。
加密DLL首先要找準(zhǔn)unity版本對應(yīng)的mono供屉,地址在這里 https://github.com/Unity-Technologies/mono
這里有個很惡心的事情行冰,unity的mono版本并不是按小版本分的,比如我想找unity4.6.1 對應(yīng)的mono那么它就沒有伶丐,unity只提供4.3x 或者 4.6x 或者5.1x 這種大版本的mono .從提交時間上來看更新的很隨意啊悼做。我感覺要想找到對應(yīng)的unity版本,可以根據(jù)unity這個版本發(fā)布的時候哗魂,然后在github上找對應(yīng)時間的mono版本肛走。。
如下圖所示录别,打開網(wǎng)頁后朽色,找到對應(yīng)的branches版本, 這里選擇unity-4.6 或者 unity-5.1 這兩個版本我已經(jīng)測試通過组题。別的版本希望大家都能來參與測試葫男。
在說說說惡心的地方,我下載4.6以后崔列,把mono編譯出來梢褐。放在unity4.6.1的打出來的包里 死活會報錯旺遮, 但是4.6.6就沒問題了。盈咳。 不過還好我這里兩個項目 一個是用unity4.6.6還有一個是unity5.1.1目前都沒出現(xiàn)問題耿眉。
下面我都用unity4.6舉例,其他版本原理都一樣我就不贅述了鱼响。
1.github下載下來對應(yīng)的mono解壓放在本地鸣剪,在終端里先cd到這個目錄下。
2.把打包腳本拖入終端中(注意腳本的路徑)丈积,然后就開始耐心等待吧筐骇。估計5分鐘左右就OK了。
3.打包腳本分兩種江滨, 一個是 arm的拥褂,還有一個是x86,執(zhí)行build_runtime_android.sh 就可以了牙寞, 它會自動調(diào)用
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下有些手機進游戲卡死。后來經(jīng)過一番分析用狱,原來是x86的編譯選項和arm不一樣运怖。如下圖所示,在X86.sh 這里只把-g去掉就行夏伊。摇展。別的什么都別改。切記切記D缬恰S搅!
然后在下面把這兩句代碼注釋掉鲁森,不然編譯的時間就要增加了祟滴。
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埠偿,解密算法很簡單就是讓字節(jié)下標(biāo)為1的字節(jié)-1透罢。
如果你要熱更DLL時一定要注意!冠蒋!這里一定要先判斷一下name是否為NULL 不然使用System.Reflection.Assembly.Load 在Android平臺反射調(diào)用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 分鐘左右就能編譯完成抖剿。對應(yīng)會會放在mono根目錄build的文件夾里朽寞。然后回到生成的adnroid工程中,把libmono.so 分別放在x86和armeabi-v7a文件夾下斩郎。因為我項目用了slua所以這里也會有一些第三方的.so
再說說自動化的問題脑融,DLL每次代碼變更都會重新生成一個新的,那么我總不能每次都手動加密DLL然后在手動的拷貝到assets下面吧缩宜。肘迎。
再說一句,我的項目在處理自動化打包時用的是adnroid的ant打包锻煌。也就是先把unity導(dǎo)出成一個android 工程妓布。然后在打包。所以我的自動化就可以是當(dāng)android工程生成后宋梧,然后把dll讀取到內(nèi)存里匣沼,加密后在重新寫到原來工程的位置上。如果有朋友不太懂自動化捂龄,可以在我博客里搜索一下释涛,以前我有寫過。
http://www.xuanyusong.com/archives/3384 環(huán)境變量如果你不會加的話倦沧,也可以看我這篇文章唇撬。
這段代碼的意思就是當(dāng)eclipse的android工程生成后,緊接著就給dll加密展融。局荚。字節(jié)一變那么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腳本來自動調(diào)用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工程中對應(yīng)的位置
string inpath = path +"/"+ PlayerSettings.productName +"/assets/bin/Data/Managed/Assembly-CSharp.dll";
if(File.Exists(inpath)){
//先讀取沒有加密的dll
byte[] bytes = File.ReadAllBytes (inpath);
//字節(jié)偏移 DLL就加密了耀态。
bytes [0] += 1;
//在寫到原本的位置上
File.WriteAllBytes (inpath, bytes);
}
}
然后還有前面我們編譯出來的兩個 mono.so 也要在這里自動化一并拷貝到這個工程對應(yīng)的目錄下面(可以在shell里拷貝,也可以在C#里拷貝)暂雹。 接下來就調(diào)用自動打包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
轉(zhuǎn)載請注明: 雨松MOMO 2015年07月02日
于 雨松MOMO程序研究院 發(fā)表