前段时间编译了一下Unity的Mono,看了很多相关的文章,也遇到很多新坑。所以来总结一下,加深自己对Mono的理解

为什么Unity可以跨平台运行呢

通常Unity的脚本有C#、JS、Boo。不过现在官方都只推荐使用C#了,为什么Unity可以支持用不同的语言编写代码呢?因为它使用了一种叫CIL的中间语言,C#、JS、Boo最终会被编译成一种叫IL的中间语言,然后通过Mono运行时编译成原生代码运行。

什么是Mono JIT

JIT就是及时编译(just in time),能把IL及时编译成原生代码运行,跟解释语言又有些不同,JIT不是逐行解释执行,而是会把解释过的代码缓存起来,以备下次运行,因此从理论上说JIT可以接近以前的纯编译技术。Unity的跨平台是运行在Mono虚拟机上的,在安卓上可以通过JIT实现动态编译IL Assembly来热更新代码,但是在IOS平台,由于苹果公司禁用了JIT,只能通过AOT提前静态编译后执行。

Unity编译出来的IL Assembly在哪里呢

以C#为例,Unity会编译出以下DLL

  1. Assembly-CSharp-filepass-vs.csproj
  2. Assembly-CSharp-Editor-filepass-vs.csproj
  3. Assembly-CSharp-vs.csproj
  4. Assembly-CSharp-Editor-vs.csproj

根据官方的解释,它们的编译顺序如下:

  1. 所有在Standard Assets、Pro Standard Assets或者Plugins文件夹中的脚本会产生一个Assembly-CSharp-filepass-vs.csproj文件,并且先编译
  2. 所有在Standard Assets/Editor、Pro Standard Assets/Editor或者Plugins/Editor文件夹中的脚本产生Assembly-CSharp-Editor-filepass-vs.csproj工程文件,接着编译;
  3. 所有在Assets/Editor外面的,并且不在(1),(2)中的脚本文件(一般这些脚本就是我们自己写的非编辑器扩展脚本)会产生Assembly-CSharp-vs.csproj工程文件,被编译;
  4. 所有在Assets/Editor中的脚本产生一个Assembly-CSharp-Editor-vs.csproj工程文件,被编译。

这些DLL都被放在Unity的缓存文件夹Library/ScriptAssemblies下。

打包成APK后,assembly会被放在assets\bin\Data\Managed文件夹下,很容易被人反编译、破解

如何增加被破解的难度

通常的方式都是加壳,一层不够再多加几层。但也只能是防止大部分破解,还是很难防止高手破解的。或者说让高手破解起来也更累,绕晕他

第一层,如果客户端用的热更新脚本是Lua、或者JS、C#,先把热更新文件加密打包成二进制。然后在C#里写解密代码。但是人家破解了你的DLL,知道了解密算法就可以逆向出源码了所以还可以加入第二层

第二层,安卓端的IL Assembly是通过libmono.so加载进来用JIT编译执行的,可以把DLL加密,然后在libmono.so中写解密算法。然而,so也是可以被反编译的,ida pro工具可以把C源代码反编译出来

第三层,加密SO,主要原理是通过加密ELF文件的section段的方式进行加密,ELF文件是Linux上的可执行程序,so就是一个ELF文件。然后在so初始化的时候进行解密 具体方式可以参考 简单粗暴的so加解密实现Unity3D研究院之Android二次加密.so二次加密DLL(八十二)

如何编译出Unity的libmono.so

我在公司是用ubuntu编译的,现在是在家写文章,所以就用家里的Mac截图,但环境跟步骤还是会按照Ubuntu的来

准备环境

  1. Linux,我用的Ubuntu (最好是32位) 下载地址: 网易Ubuntu镜像
  2. VMware Workstation
  3. VMware Tools —— 用于从虚拟机中拷贝文件,攻略
  4. JDK
  5. NDK
  6. 在Linux上直接运行:sudo apt-get install autoconf automake libtool git pkg-config
  7. sudo apt-get install libc6-dev-i386 lib32z1(如果是64位的ubuntu还需要安装这两个库)

需要了解一些简单的Linux指令:

  1. sudo 使用root权限执行
  2. vi - 用于编辑文本,最好去熟悉下vi里面的一些指令:i、w、q、!等等,ubuntu里使用gedit
  3. unzip 解压缩zip
  4. tar -xzvf file.tar.gz 解压缩tar.gz
  5. cd 前往某个目录
  6. ls 列出目录下的文件列表
  7. gedit ~/.bashrc 中修改环境变量
  8. mv 移动文件

1.下载Mono,去这个地址找到想对应的Unity版本的Mono https://github.com/Unity-Technologies/mono

2.存放到一个目录,好找就行,我放在了/Users/lijia/TestMono下,用unzip mono-unity-5.4.zip解压缩出来

3.cd mono-unity-5.4/external/buildscripts目录下,打开build_runtime_android.sh

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

找到该行,发现该Mono的版本需要的NDK是r10e,所以我们先去下载NDK r10e(不能是r10c,一定要是r10e)

4.把NDK解压,然后用mv指令移动到/opt下

sudo mv android-ndk-r10e /opt

设置好环境变量,在ubuntu上使用gedit ~/.bashrc打开文件编辑

输入如下配置:

NDK_ROOT=/opt/android-ndk-r10e
NDK=$NDK_ROOT
ANDROID_NDK_ROOT=$NDK_ROOT
export NDK_ROOT NDK ANDROID_NDK_ROOT

然后使用:source ~/.bashrc让环境变量生效

5.把build_runtime_android.sh复制到mono-unity-5.4下

6.编辑mono-unity-5.4下的envsetup.sh文件(没有就创建一个)

在里面写上一行

export ANDROID_NDK_ROOT=/opt/android-ndk-r10e

7.修改mono-unity-5.4/build_runtime_android.sh文件中的这一行后面改成刚刚我们创建的文件的路径

perl ${BUILDSCRIPTSDIR}/PrepareAndroidSDK.pl -ndk=r10e -env=envsetup.sh && source /Users/lijia/TestMono/mono-unity-5.4/envsetup.sh

接着把这一行

KRAIT_PATCH_PATH="${CWD}/../../android_krait_signal_handler/build"

修改为

KRAIT_PATCH_PATH="${CWD}/android_krait_signal_handler/build"

不然会在你的mono-unity-5.4文件夹的上一层的上一层创建编译工具,这是一个坑,看别人文章知道的

注释掉这2行可以提高编译速度,不必要的cpu架构可以不编译

#clean_build “$CCFLAGS_ARMv5_CPU” “$LDFLAGS_ARMv5” “$OUTDIR/armv5”

#clean_build “$CCFLAGS_ARMv6_VFP” “$LDFLAGS_ARMv5” “$OUTDIR/armv6_vfp”

还可以修改一些编译参数,编译releace版本。

编辑mono-unity-5.4/build_runtime_android.sh找到 -fpic -g 把-g改成O2(这里不是数字02,而是大写字母O2)

编辑mono-unity-5.4/external/buildscripts/build_runtime_android_x86.sh中的 -fpic -g 把-g去掉就行

8.接下来就开始第一次运行mono-unity-5.4/build_runtime_android.sh,命令行输入:./build_runtime_android.sh

这里有个坑!

我发现它在给我下载ndk r13b... Why?build_runtime_android.sh里面不是说好了是r10e的吗?

果断先用ctrl+c停止掉,遇到询问请按回车

然后开始分析

build_runtime_android.sh首先会去git一个编译工具库,也就是android_krait_signal_handler,我们在第7步修改了过它的路径还记得吗,然后去mono-unity-5.4/android_krait_signal_handler/build下找到build.pl

打开后发现他去获取的ndk是r13b

这里手动改成r10e(跟环境变量一致)

然后继续修改build.pl的第一行

#!/usr/bin/env perl -w
改为
#!/usr/bin/perl -w

都是教训..

9.然后继续运行mono-unity-5.4/build_runtime_android.sh,命令行输入:./build_runtime_android.sh

10.出现了很长的命令行滚动,有点像黑客帝国的感觉,有木有~ 证明已经开始编译了

11.如果顺利的话,最后会出现了一个BUILD SUCCESS! 恭喜你,编译成功了..

12.如果不顺利,可以在我博客下留言,我有空的时候会回复你

编译完了Mono,你可以把加密的算法写在image.c文件里,重新编译出来使用。

如果是安卓端,也可以在Mono中写热更新DLL的代码,比如判断热更路径下存在新的DLL,那在JIT阶段就直接编译新DLL执行。(不推荐,因为下载完DLL,要重新拉起一遍程序才能重新运行Mono去加载新DLL)

总结

  1. Unity的跨平台是基于Mono运行的
  2. libmono.so通过JIT加载DLL执行
  3. 加密DLL的代码可以写在mono中,重新编译出libmono.so使用
  4. 编译Mono的过程会遇到很多坑,有的别人踩过的,有的是新坑,遇到坑的过程中不要心急,仔细阅读一下错误日志以及工具的源码
  5. 现在很多项目都使用IL2CPP,直接把IL转成C++代码,然后编译成目标平台下原生代码

参考:

  1. http://www.xuanyusong.com/archives/3553
  2. Unity3D-重新编译Mono加密DLL
  3. http://blog.csdn.net/qq_27772057/article/details/51945700
  4. http://csftech.logdown.com/posts/452269-android-unity-encryption

Unity中的Mono & Linux上编译Mono的流程的更多相关文章

  1. linux上安装mono发布.net网站步骤

    在linux上部署mono 1.自己安装好linux 2.使用桥接方式,让虚拟机和本机在一个局域网内 3.安装apache服务器 4.安装libgdiplug 5.安装mono 6.安装xsp 7.安 ...

  2. 尝试在Linux上编译KestrelHttpServer

    Kestrel是目前在非Windows平台上运行ASP.NET 5应用程序的唯一可用Web服务器,但微软似乎将它冷落在一边,源代码更新很慢. 今天试着在Linux上编译Kestrel的源代码,遇到了很 ...

  3. 在Linux上编译Hadoop-2.4.0

    目录 目录 1 1. 前言 1 2. 安装依赖 1 2.1. 安装ProtocolBuffer 2 2.2. 安装CMake 2 2.3. 安装JDK 2 2.4. 安装Maven 3 3. 编译Ha ...

  4. Linux上编译安装PHP

    这篇文章主要介绍了关于Linux上编译安装PHP,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下 之前在服务器上编译安装了PHP运行环境,但是安装完过了一段时间就差不多忘记了,只是零零星 ...

  5. 麒麟Linux上编译subversion

    麒麟Linux上编译subversion svn-1.7不支持svn info --show-item=revision[1]获取revision. svn-1.12开始不能保存密码stackover ...

  6. Unity中各个平台的预编译的运用方式

    1,unity中官方文档的一个操纵关键词   Platform Dependent Compilation 2,常用的预编译关键词    UNITY_EDITOR    编辑器调用.UNITY_STA ...

  7. kali Linux 上编译并使用RFID核弹——proxmark3

    你还在在Windows下使用proxmark3?弱爆了! 本文作者:i春秋签约作家——冰尘 作为一个标准的日天日地日空气的(单身贵族泰迪)物理黑客Proxmark3这么高大上的东西应该是在键盘敲打声中 ...

  8. 在Unity中高效工作(上)

    原地址:http://www.unity蛮牛.com/thread-19974-1-1.html 编的话:感谢做编程的IT朋友,帮我翻译文章,我又稍稍做了些修改.给点儿掌声哩.欢迎大家多多评论呦. 我 ...

  9. 在Linux上编译TCMalloc

    TCMalloc(Thread-Caching Malloc)与标准glibc库的malloc实现一样的功能,但是TCMalloc在效率和速度效率都比标准malloc高很多.TCMalloc是goog ...

随机推荐

  1. 原创:TSP问题解决方案-----禁忌搜索算法C实现

    本文着重于算法的实现,对于理论部分可自行查看有关资料可以简略参考该博文:http://blog.csdn.net/u013007900/article/details/50379135 本文代码部分基 ...

  2. 跨主机使用 Rex-Ray volume - 每天5分钟玩转 Docker 容器技术(77)

    上一节我们在 docker1 上的 MySQL 容器中使用了 Rex-Ray volume mysqldata,更新了数据库.现在容器已经删除,今天将演示在 docker2 中重新使用这个卷. 在 d ...

  3. pongo英雄会-幸运数题解

    显然我们只要知道1~x范围有多少幸运数(用f(x)表示),lucky(x,y)=f(y)-f(x-1). 解法1. 计算排列数 由于y<=1000000000这个规模,我们不能暴力验证每个数是否 ...

  4. Struts201---环境搭配

    开发工具:Eclipse   Struts版本:2.3.24 最近在学SSH框架,SSH是 struts+spring+hibernate的一个集成框架,是目前比较流行的一种Web应用程序开源框架.集 ...

  5. wampserver里面出现403错误的问题解决方法

    今天再装wampserver的时候,不能进入localhost和phpmyadmin,提示403错误,我自己是win10系统,已通过以下方法解决了: 1.第一个问题,就是wampserver没有切换到 ...

  6. SSM框架整合项目 :租房管理系统

    使用ssm框架整合,oracle数据库 框架: Spring SpringMVC MyBatis 导包: 1, spring 2, MyBatis 3, mybatis-spring 4, fastj ...

  7. 关于string.h中字符串的操作

     string.h中字符操作的函数 注意:**对字符数组的多次操作需要进行赋初值.或者善于用memset()函数进行清空数组的操作.**     否则容易出现错误. string.h文件中函数的用法加 ...

  8. win10 uwp 绑定密码

    win10 下,密码框无法绑定到ViewModel,Password是不可以绑定. 我们可以自己使用简单方法去绑定 我们之前在WPF 使用绑定密码框,我写了一篇,关于如何绑定,我提供一个我自己试了可以 ...

  9. win10 uwp 存放网络图片到本地

    有时候我们的网络很垃圾,我的的UWP要在第一次打开网络图片,就把图片存放到本地,下次可以从本地打开. 有时候用户使用的是流量网络,不能每次都联网下载. 我们不得在应用存放用户打开的图片. 这就是先把图 ...

  10. LeetCode 219. Contains Duplicate II (包含重复项之二)

    Given an array of integers and an integer k, find out whether there are two distinct indices i and j ...