Unity3D DLL加密

Unity3D打包android應用程序時,如果不對DLL加密,很容易被反編譯,導致代碼的泄露。通常的做法是通過加密DLL或者對代碼進行混淆。本文的所要探討的是通過加密的方式來對DLL進行保護,並詳細記錄加密的操作過程。


主要參考

    雨鬆的博文:http://www.xuanyusong.com/archives/3553 

    http://csftech.logdown.com/posts/452269-android-unity-encryption

    這兩篇文章已經詳細介紹了加密的過程,但是還是有些坑和有些操作沒有給出。


原理說明

所有的代碼編譯後是在apk\assets\bin\Data\Managed\Assembly-CSharp.dll下,要做的就是對這個DLL進行加密,Assembly-CSharp.dll由libmono.so加載,所以需要在libmono.so中對加密過的Assembly-CSharp.dll進行解密,幸好unity提供了mono的代碼可以進行編譯修改。當然對於libmono.so也存在被反編譯的風險,本文暫不考慮。


準備

  • linux系統。本文選擇採用的是Ubuntu14.04,虛擬機也可以,另外可以用Windows + Cygwin進行編譯,不過考慮到這樣做可能踩坑更多,果斷放棄。

  • unity mono源碼,可以在https://github.com/Unity-Technologies/mono下載,branch選擇unity4.6,直接下zip包,或者git下來都可以,下載下來的zip包爲mono-unity-4.6.zip

  • unity3d 4.6版本,本文試驗的是4.6的編譯,注意一定要安裝4.6.6+的版本,否則重編的libmono.so會報錯(坑一)。

  • android ndk, 版本可以根據unity-mono中用的版本來下載,參見unity-mono/external/buildscripts/build_runtime_android.sh, 搜一下ndk=就能找到,本文用到的是r10e,下載下來的ndk爲android-ndk-r10e-linux-x86_64.bin

  • apktools, 用來對apk進行解包簽名打包,2.0以上版本,否則打包是會報錯。


編譯mono

1)爲了方便使用root進行編譯,Ubuntu下root默認不開啓,可以使用:

    sudo passwd root

    輸兩次密碼後

    su - 

    進行登錄


2)NDK安裝

    安裝7z 

       apt-get install p7zip-full

    解壓

       7z x android-ndk-r10e-linux-x86.bin

    配置環境變量,配置方法有很多,可以修改/etc/profile或者~/.bashrc,這裏直接shell下添加臨時的環境變量,不添加後面編mono時會報找不到NDK

       export ANDROID_NDK_ROOT=/home/xubo/unity-dev/android-ndk-r10e

       export PATH=$ANDROID_NDK_ROOT:$PATH


3)檢查一下mono使用的NDK版本

vi打開mono-unity-4.6/external/buildscripts/build_runtime_android.sh可以找到

perl ${BUILDSCRIPTSDIR}/PrepareAndroidSDK.pl -ndk=r10e -env=envsetup.sh && source envsetup.sh

這裏可以確定當前的unity mono使用r10e來進行編譯的


4)安裝編譯必備的一些包

apt-get install autoconf automake bison build-essential gettext git libglib2.0 libtool perl


5)嘗試第一次編譯

    ./external/buildscripts/build_runtime_android.sh

    報錯:

        /usr/bin/env: perl -w: No such file or directory

    這裏unity-mono編譯的時候會去git 一個包android_krait_signal_handler,在external目錄下,就是這個包報錯,這個包出錯的問題很多,是個巨坑(坑二)。


    打開android_krait_signal_handler/build.pl,將第一行

        #!/usr/bin/env perl -w

    改爲

        #!/usr/bin/perl -w


    將下面行

        PrepareAndroidSDK::GetAndroidSDK(undef, undef, "r9");

    改爲實際用到的NDK

        PrepareAndroidSDK::GetAndroidSDK(undef, undef, "r10e");


    將buildscripts/PrepareAndroidSDK.pm替        換android_krait_signal_handler/PrepareAndroidSDK.pm


    打開jni/Application.mk將下兩行都刪掉   

APP_PLATFORM := android-9
NDK_TOOLCHAIN_VERSION := clang3.3

    否則會報下面的錯誤

make: execvp: /home/xubo/unity-dev/android-ndk-r10e/toolchains/arm-linux-androideabi-4.8: Permission denied


6)嘗試第二次編譯

    configure不通過,打開config.log發現

./configure: line 4546: /home/xubo/unity-dev/android-ndk-r10e/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86/bin/arm-linux-androideabi-gcc: No such file or directory

    檢查該目錄,發現文件是存在的,這裏是因爲雖然NDK是64位的,但是交叉編譯工具鏈是32位的,安裝一下,而本文采用的編譯機是64位的,安裝一下64位下運行32位可執行文件的包

    apt-get install lib32z1 lib32ncurses5 lib32bz2-1.0


7)嘗試第三次編譯,至此我們應該可以編譯成功了,但還沒涉及到加解密,注意編譯需要在mono的根目錄下進行。最終顯示如下則OK:

Build SUCCESS!
Android STATIC/SHARED libraries are found here: builds/embedruntimes/android


加密程序

加密過程可參考上面的鏈接,就是將Assembly-CSharp.dll視作普通的文件,隨便用什麼語言寫個加密的代碼,簡單的可以修改幾個字節,做偏移啥的,生成一個新的Assembly-CSharp.dll,替換原來的,這樣一般的破解軟件就沒轍了。


MONO解密

上面只是試驗了一下mono的編譯,關於將解密的代碼添加至mono還沒有做。


打開mono-unity-5.3/mono/metadata/image.c,找到mono_image_open_from_data_with_name函數修改如下

MonoImage *
mono_image_open_from_data_with_name (char *data, guint32 data_len, gboolean need_copy, MonoImageOpenStatus *status, gboolean refonly, const char *name)
{
        MonoCLIImageInfo *iinfo;
        MonoImage *image;
        char *datac;
        
        //添加如下代碼
        if(name != NULL)
        {
            if(strstr(name,"Assembly-CSharp.dll")){
                //這裏寫下你的解密的代碼,入參data是從Assembly-CSharp.dll讀文件讀出來的
                //被加密的原始數據,通過你的解密代碼生成一段新的data
            }
        }
        
        if (!data || !data_len) {
                if (status)
                        *status = MONO_IMAGE_IMAGE_INVALID;
                return NULL;
        }
        datac = data;
        if (need_copy) {
                datac = g_try_malloc (data_len);
                if (!datac) {
                        if (status)
                                *status = MONO_IMAGE_ERROR_ERRNO;
                        return NULL;
                }
                memcpy (datac, data, data_len);
        }


MONO正式編譯

正式編譯mono前,還有兩個地方要修改,不修改編譯出來的是debug版本,libmono.so有8M,

打開build_runtime_android.sh, 將下面標紅的-g給去掉,編譯release版本

CFLAGS="\
-DANDROID -DPLATFORM_ANDROID -DLINUX -D__linux__ \
-DHAVE_USR_INCLUDE_MALLOC_H -DPAGE_SIZE=0x1000 \
-D_POSIX_PATH_MAX=256 -DS_IWRITE=S_IWUSR \
-DHAVE_PTHREAD_MUTEX_TIMEDLOCK \
-fpic -g -funwind-tables \

同樣build_runtime_android_x86.sh裏面也去掉


Unity3D 簽名

別忘記了,需要unity4.6.6+的版本,本文是在unity4.6.9下測試OK。

製作一個簽名,後面在用apktool重新封包時用得到,用這個簽名對遊戲進行build。

wKiom1bT_7CxRO8WAAD5dUcSwDo177.jpg


Apktool解包封包

1)(windows下操作)確定apktool目錄下有aapt.exe,apktool.bat,apktool.jar,確定版本是2.0+

2)將生成的包例如1.apk 複製到apktool/下

3)cmd命令行下,進入apktool目錄,執行apktool d 1.apk進行解包,會在apktool下生成與包名相同的文件夾1/

4) 將加密過的Assembly-CSharp.dll覆蓋1\assets\bin\Data\Managed\Assembly-CSharp.dll

5) 將編譯過的libmono.so,注意這裏選擇armv7a/,和x86/下的,分別覆蓋1\lib\armeabi-v7a和1\lib\x86\下的libmono.so

6) 封包命令行下執行apktool b -f 1,會在1/下生成dist文件,裏頭就是新封的包,改名爲2.apk,並複製到apktool/下

7)簽名,隱去的是你要填的簽名文件名,和別名

    jarsigner -verbose -keystore ****.keystore -signedjar 2_s.apk 2.apk ****

8)2_s.apk就是你加密過的包,進行安裝測試


libmono.so加密

雨鬆還提到了libmono.so的加密,這裏先不涉及吧,strip動態庫,可能能起到相同的效果。


小結

這樣加密經過測試是OK的,可以防止一般的反編譯軟件進行破解了,對於高手可能還防不住,另外編譯mono有點心驚膽戰,android_krait_signal_handler這個工程是個坑,還是有點擔心編出來的libmono.so有咩有啥隱患,所以這樣弄需要在各種android機子上多測試。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章