最新消息: USBMI致力于为网友们分享Windows、安卓、IOS等主流手机系统相关的资讯以及评测、同时提供相关教程、应用、软件下载等服务。

自定义Mono,实现Unity Android平台代码更新

IT圈 admin 1浏览 0评论

自定义Mono,实现Unity Android平台代码更新

原理

在做这个之前,需要了解:

1、Unity是基于Mono的,我们写的代码都被编译成DLL,然后由Mono读取这个DLL并解析运行。

2、在Mono眼里,DLL和普通的资源文件没什么区别。


去年做过了修改Mono 来实现加密DLL,防破解。

Unity3d 加密 Assembly-CSharp.dll (Android平台) 全记录


现在项目有代码热更的需求,而去年的虚拟机被删除了。就重新走了一遍流程。

参考了以下文章:

.html


转自  

在 Android 中,由 libmono.so 来加载 Assembly-CSharp.dll 。

libmono.so  这就是 Mono 了 。



Unity3d 是基于 Mono2.0 的,而 Mono2.0是免费开源的。所以基于各种开源协议 ,Unity 官方也将自己修改过的 Mono 开源出来,我们下载过来然后修改 重新编译出自己的 libmono.so 。

项目托管在 Github 上,项目地址:


我们项目用的是Unity5.3.2,所以下载mono的分支

.3


了解到一些原理背景后就可以开始进行操作了。

1、安装ubuntu系统


Ubuntu 系统下载:


    .04.1/ubuntu-14.04.1-desktop-i386.iso?_ga=1.187436840.1241524278.1457318071  


如果你对Ubuntu不是很熟悉的话,我建议重新创建一个新的虚拟机。

我这里安装的是32位的Ubuntu14.


安装好Ubuntu之后,首先要设置一下软件源为国内的镜像站,这样安装软件会很快。

在左边的快速启动面板中点击“软件中心”按钮,

再把鼠标移到最上边出现菜单栏,点“编辑-软件源”

转自  

在出来的软件源面板下边,点击“下拉列表,选择“其他站点”;


在出来的服务器列表中,选择搜狐、163 或 cn99 的站点都可以,然后点右下角的“选择服务器”按钮返回;



2、下载ANDROID_NDK

安装完 Ubuntu 后,在 Ubuntu 中   ,注意32 和64位区别

64 位下载 :


32位下载  :


sudo su 切换到root安装

./android-ndk-r10e-linux-x86.bin

安装后在安装目录里面找到 RELEASE.txt ,里面记录着NDK 完整版本号,修改为 r10e

(Mono的编译脚本是读取这个RELEASE.txt中记录的版本号,然后和编译脚本中填写的版本号做匹配的,如果不匹配就会去Google下载)


设置环境变量 ANDROID_NDK_ROOT

sudo gedit /etc/bashrc

添加一行

export ANDROID_NDK_ROOT=/home/captain/Downloads/android-ndk-r10e;

让环境变量立即生效

source /etc/bashrc

测试是否添加成功

echo $ANDROID_NDK_ROOT 


3、编译 Development 版本的 libmono.so

1、从 Github 下载 unity-mono,我这里下载的5.3版本

.3



如果你也是Unity5.3版本,那么可以直接Clone我编译好的Mono。

.3_Android_DLL_HotFix


2、下载好之后解压,然后拷贝编译脚本到mono根目录

cp external/buildscripts/build_runtime_android.sh ./


3、运行编译脚本,提示没有安装git,安装git

sudo apt-get install autoconf

4、修改脚本build_runtime_android.sh

sudo gedit build_runtime_android.sh

搜索KRAIT_PATCH_PATH 修改

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

搜索(cd "$KRAIT_PATCH_PATH" &&修改

(cd "$KRAIT_PATCH_PATH" && perl ./build.pl)

5、android_krait_signal_handler/build.pl 删除第一行注释

转自  

6、安装其它的依赖库

* autoconf  
* automake  
* bison  
* gcc  
* gettext  
* glib >= 2.0  
* libtool  
* make  
* perl

7、再次执行脚本,编译成功。


这个so 就是mono的库,我们可以用它替换掉unity自带的。


4、修改mono源码,读取下载的新版本DLL

找到 /metadata/image.c 这个文件

找到

mono_image_open_from_data_with_name

修改如下:

static FILE* OpenFileWithPath( const char* path ){const char* fileMode ="rb";return fopen(path, fileMode );}static char* ReadStringFromFile(const char* pathName,int* size){FILE* file = OpenFileWithPath( pathName);if (file == NULL)return 0;fseek(file, 0, SEEK_END);int length = ftell(file);fseek(file, 0, SEEK_SET);if (length < 0){fclose( file );return 0;}*size = length;char* outData = g_try_malloc (length);int readLength = fread(outData, 1, length, file);fclose(file);if (readLength != length){//if(readLength == length){//    JNI_OnLoad(0,0);//    JNI_OnUnload(0,0);//}g_free (outData);return 0;}return outData;}MonoImage *
mono_image_open_from_data_with_name (char *data, guint32 data_len, gboolean need_copy, MonoImageOpenStatus *status, gboolean refonly, const char *name)
{//修改开始1int datasize=0;if(strstr(name,"Assembly-CSharp.dll")){//重新计算路径const char* _pack = strstr(name,"com.");const char* _pfie = strstr(name,"-");char _name[512];memset(_name,0,512);int _len0 = (int)(_pfie - _pack);memcpy(_name , "/data/data/",11);memcpy(_name + 11, _pack,_len0);memcpy(_name + 11 + _len0 ,"/files/Assembly-CSharp.dll",26);g_message("momo: path = %s\n", _name);char* bytes = ReadStringFromFile (_name,&datasize);if(datasize > 0){data = bytes;data_len = datasize;}}if(strstr(name,"Assembly-CSharp-firstpass.dll")){//重新计算路径const char* _pack = strstr(name,"com.");const char* _pfie = strstr(name,"-");char _name[512];memset(_name,0,512);int _len0 = (int)(_pfie - _pack);memcpy(_name , "/data/data/",11);memcpy(_name + 11, _pack,_len0);memcpy(_name + 11 + _len0 ,"/files/Assembly-CSharp-firstpass.dll",36);g_message("momo: path = %s\n", _name);char* bytes = ReadStringFromFile (_name,&datasize);if(datasize > 0){data = bytes;data_len = datasize;}}//修改结束1MonoCLIImageInfo *iinfo;MonoImage *image;char *datac;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);}//修改开始2if(datasize > 0 && data != 0){g_free (data); }//修改结束2image = g_new0 (MonoImage, 1);image->raw_data = datac;image->raw_data_len = data_len;image->raw_data_allocated = need_copy;image->name = (name == NULL) ? g_strdup_printf ("data-%p", datac) : g_strdup(name);iinfo = g_new0 (MonoCLIImageInfo, 1);image->image_info = iinfo;image->ref_only = refonly;image->ref_count = 1;image = do_mono_image_load (image, status, TRUE, TRUE);if (image == NULL)return NULL;return register_image (image);
}

修改的效果是,mono在读取完默认位置的dll之后,我们添加的代码会判断有没有下载的新版本的dll位于 /data/data/xx/file 目录,如果有,就读取新版本的dll替换掉默认位置的dll。


保存后,重新编译so文件。


5、编译Release版本

修改所有的 build_runtime_android.sh

找到 -fpic -g 去掉 -g

这样再编译就得到Release版本的so文件

转自  

6、导出工程,可以用脚本自动替换so文件。

/** * 文件名:BuildPostprocessor.cs * Des:在导出Eclipse工程之后对替换mono.so* Author:Captain * **/using UnityEngine;
using UnityEditor;
using UnityEditor.Callbacks;
using System.IO;public class BuildPostprocessor
{[PostProcessBuildAttribute(1)]public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject){if (target == BuildTarget.Android && (!pathToBuiltProject.EndsWith(".apk"))){Debug.Log("target: " + target.ToString());Debug.Log("pathToBuiltProject: " + pathToBuiltProject);Debug.Log("productName: " + PlayerSettings.productName);Debug.Log("Current is : " + EditorUserBuildSettings.development.ToString());//替换 libmono.so;  if (EditorUserBuildSettings.development){string armv7a_so_path = pathToBuiltProject + "/" + PlayerSettings.productName + "/" + "libs/armeabi-v7a/libmono.so";File.Copy(Application.dataPath + "/HotFix/Editor/libs/development/armeabi-v7a/libmono.so", armv7a_so_path, true);string x86_so_path = pathToBuiltProject + "/" + PlayerSettings.productName + "/" + "libs/x86/libmono.so";File.Copy(Application.dataPath + "/HotFix/Editor/libs/development/x86/libmono.so", x86_so_path, true);}else{string armv7a_so_path = pathToBuiltProject + "/" + PlayerSettings.productName + "/" + "libs/armeabi-v7a/libmono.so";File.Copy(Application.dataPath + "/HotFix/Editor/libs/release/armeabi-v7a/libmono.so", armv7a_so_path, true);string x86_so_path = pathToBuiltProject + "/" + PlayerSettings.productName + "/" + "libs/x86/libmono.so";File.Copy(Application.dataPath + "/HotFix/Editor/libs/release/x86/libmono.so", x86_so_path, true);}Debug.Log("HotFix libmono.so Success !!");}}
}


如果你也是Unity5.3版本,那么可以直接Clone我编译好的Mono。

.3_Android_DLL_HotFix

自定义Mono,实现Unity Android平台代码更新

原理

在做这个之前,需要了解:

1、Unity是基于Mono的,我们写的代码都被编译成DLL,然后由Mono读取这个DLL并解析运行。

2、在Mono眼里,DLL和普通的资源文件没什么区别。


去年做过了修改Mono 来实现加密DLL,防破解。

Unity3d 加密 Assembly-CSharp.dll (Android平台) 全记录


现在项目有代码热更的需求,而去年的虚拟机被删除了。就重新走了一遍流程。

参考了以下文章:

.html


转自  

在 Android 中,由 libmono.so 来加载 Assembly-CSharp.dll 。

libmono.so  这就是 Mono 了 。



Unity3d 是基于 Mono2.0 的,而 Mono2.0是免费开源的。所以基于各种开源协议 ,Unity 官方也将自己修改过的 Mono 开源出来,我们下载过来然后修改 重新编译出自己的 libmono.so 。

项目托管在 Github 上,项目地址:


我们项目用的是Unity5.3.2,所以下载mono的分支

.3


了解到一些原理背景后就可以开始进行操作了。

1、安装ubuntu系统


Ubuntu 系统下载:


    .04.1/ubuntu-14.04.1-desktop-i386.iso?_ga=1.187436840.1241524278.1457318071  


如果你对Ubuntu不是很熟悉的话,我建议重新创建一个新的虚拟机。

我这里安装的是32位的Ubuntu14.


安装好Ubuntu之后,首先要设置一下软件源为国内的镜像站,这样安装软件会很快。

在左边的快速启动面板中点击“软件中心”按钮,

再把鼠标移到最上边出现菜单栏,点“编辑-软件源”

转自  

在出来的软件源面板下边,点击“下拉列表,选择“其他站点”;


在出来的服务器列表中,选择搜狐、163 或 cn99 的站点都可以,然后点右下角的“选择服务器”按钮返回;



2、下载ANDROID_NDK

安装完 Ubuntu 后,在 Ubuntu 中   ,注意32 和64位区别

64 位下载 :


32位下载  :


sudo su 切换到root安装

./android-ndk-r10e-linux-x86.bin

安装后在安装目录里面找到 RELEASE.txt ,里面记录着NDK 完整版本号,修改为 r10e

(Mono的编译脚本是读取这个RELEASE.txt中记录的版本号,然后和编译脚本中填写的版本号做匹配的,如果不匹配就会去Google下载)


设置环境变量 ANDROID_NDK_ROOT

sudo gedit /etc/bashrc

添加一行

export ANDROID_NDK_ROOT=/home/captain/Downloads/android-ndk-r10e;

让环境变量立即生效

source /etc/bashrc

测试是否添加成功

echo $ANDROID_NDK_ROOT 


3、编译 Development 版本的 libmono.so

1、从 Github 下载 unity-mono,我这里下载的5.3版本

.3



如果你也是Unity5.3版本,那么可以直接Clone我编译好的Mono。

.3_Android_DLL_HotFix


2、下载好之后解压,然后拷贝编译脚本到mono根目录

cp external/buildscripts/build_runtime_android.sh ./


3、运行编译脚本,提示没有安装git,安装git

sudo apt-get install autoconf

4、修改脚本build_runtime_android.sh

sudo gedit build_runtime_android.sh

搜索KRAIT_PATCH_PATH 修改

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

搜索(cd "$KRAIT_PATCH_PATH" &&修改

(cd "$KRAIT_PATCH_PATH" && perl ./build.pl)

5、android_krait_signal_handler/build.pl 删除第一行注释

转自  

6、安装其它的依赖库

* autoconf  
* automake  
* bison  
* gcc  
* gettext  
* glib >= 2.0  
* libtool  
* make  
* perl

7、再次执行脚本,编译成功。


这个so 就是mono的库,我们可以用它替换掉unity自带的。


4、修改mono源码,读取下载的新版本DLL

找到 /metadata/image.c 这个文件

找到

mono_image_open_from_data_with_name

修改如下:

static FILE* OpenFileWithPath( const char* path ){const char* fileMode ="rb";return fopen(path, fileMode );}static char* ReadStringFromFile(const char* pathName,int* size){FILE* file = OpenFileWithPath( pathName);if (file == NULL)return 0;fseek(file, 0, SEEK_END);int length = ftell(file);fseek(file, 0, SEEK_SET);if (length < 0){fclose( file );return 0;}*size = length;char* outData = g_try_malloc (length);int readLength = fread(outData, 1, length, file);fclose(file);if (readLength != length){//if(readLength == length){//    JNI_OnLoad(0,0);//    JNI_OnUnload(0,0);//}g_free (outData);return 0;}return outData;}MonoImage *
mono_image_open_from_data_with_name (char *data, guint32 data_len, gboolean need_copy, MonoImageOpenStatus *status, gboolean refonly, const char *name)
{//修改开始1int datasize=0;if(strstr(name,"Assembly-CSharp.dll")){//重新计算路径const char* _pack = strstr(name,"com.");const char* _pfie = strstr(name,"-");char _name[512];memset(_name,0,512);int _len0 = (int)(_pfie - _pack);memcpy(_name , "/data/data/",11);memcpy(_name + 11, _pack,_len0);memcpy(_name + 11 + _len0 ,"/files/Assembly-CSharp.dll",26);g_message("momo: path = %s\n", _name);char* bytes = ReadStringFromFile (_name,&datasize);if(datasize > 0){data = bytes;data_len = datasize;}}if(strstr(name,"Assembly-CSharp-firstpass.dll")){//重新计算路径const char* _pack = strstr(name,"com.");const char* _pfie = strstr(name,"-");char _name[512];memset(_name,0,512);int _len0 = (int)(_pfie - _pack);memcpy(_name , "/data/data/",11);memcpy(_name + 11, _pack,_len0);memcpy(_name + 11 + _len0 ,"/files/Assembly-CSharp-firstpass.dll",36);g_message("momo: path = %s\n", _name);char* bytes = ReadStringFromFile (_name,&datasize);if(datasize > 0){data = bytes;data_len = datasize;}}//修改结束1MonoCLIImageInfo *iinfo;MonoImage *image;char *datac;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);}//修改开始2if(datasize > 0 && data != 0){g_free (data); }//修改结束2image = g_new0 (MonoImage, 1);image->raw_data = datac;image->raw_data_len = data_len;image->raw_data_allocated = need_copy;image->name = (name == NULL) ? g_strdup_printf ("data-%p", datac) : g_strdup(name);iinfo = g_new0 (MonoCLIImageInfo, 1);image->image_info = iinfo;image->ref_only = refonly;image->ref_count = 1;image = do_mono_image_load (image, status, TRUE, TRUE);if (image == NULL)return NULL;return register_image (image);
}

修改的效果是,mono在读取完默认位置的dll之后,我们添加的代码会判断有没有下载的新版本的dll位于 /data/data/xx/file 目录,如果有,就读取新版本的dll替换掉默认位置的dll。


保存后,重新编译so文件。


5、编译Release版本

修改所有的 build_runtime_android.sh

找到 -fpic -g 去掉 -g

这样再编译就得到Release版本的so文件

转自  

6、导出工程,可以用脚本自动替换so文件。

/** * 文件名:BuildPostprocessor.cs * Des:在导出Eclipse工程之后对替换mono.so* Author:Captain * **/using UnityEngine;
using UnityEditor;
using UnityEditor.Callbacks;
using System.IO;public class BuildPostprocessor
{[PostProcessBuildAttribute(1)]public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject){if (target == BuildTarget.Android && (!pathToBuiltProject.EndsWith(".apk"))){Debug.Log("target: " + target.ToString());Debug.Log("pathToBuiltProject: " + pathToBuiltProject);Debug.Log("productName: " + PlayerSettings.productName);Debug.Log("Current is : " + EditorUserBuildSettings.development.ToString());//替换 libmono.so;  if (EditorUserBuildSettings.development){string armv7a_so_path = pathToBuiltProject + "/" + PlayerSettings.productName + "/" + "libs/armeabi-v7a/libmono.so";File.Copy(Application.dataPath + "/HotFix/Editor/libs/development/armeabi-v7a/libmono.so", armv7a_so_path, true);string x86_so_path = pathToBuiltProject + "/" + PlayerSettings.productName + "/" + "libs/x86/libmono.so";File.Copy(Application.dataPath + "/HotFix/Editor/libs/development/x86/libmono.so", x86_so_path, true);}else{string armv7a_so_path = pathToBuiltProject + "/" + PlayerSettings.productName + "/" + "libs/armeabi-v7a/libmono.so";File.Copy(Application.dataPath + "/HotFix/Editor/libs/release/armeabi-v7a/libmono.so", armv7a_so_path, true);string x86_so_path = pathToBuiltProject + "/" + PlayerSettings.productName + "/" + "libs/x86/libmono.so";File.Copy(Application.dataPath + "/HotFix/Editor/libs/release/x86/libmono.so", x86_so_path, true);}Debug.Log("HotFix libmono.so Success !!");}}
}


如果你也是Unity5.3版本,那么可以直接Clone我编译好的Mono。

.3_Android_DLL_HotFix

发布评论

评论列表 (0)

  1. 暂无评论