2019-12-09 16:51:10 zhunju0089 阅读数 10
  • Unity3D入门到精通-(4)Unity热更新

    本次系列课程的目标是让Unity3D初学者掌握C语言,Untiy调用lua脚本深入理解uLua框架实现原理,并根据项目的特点实现相应的热更新方案。 适合对象:Unity初学开发者,Unity中级开发者,所有对游戏开发有兴趣的人员。 学习条件:有一定的Unity3D基础,了解C#的基本开发知识。

    2214 人正在学习 去看看 张刚

1.打开vs2017新建新项目

2.选择 Windows桌面>动态链接库(DLL)

3.定义一个C++加法函数

4.生成DLL动态链接库

5.在unity中Assets下创建plugins文件夹,将DLL放入其中(必须放到plugins下,否则unity不认)

6.在unity下新建一个Test.cs脚本

using UnityEngine;
using System.Runtime.InteropServices;

public class Test : MonoBehaviour
{
    [DllImport("Dll1")]
    public static extern int add(int a, int b);
    void Start()
    {
        print(add(10, 20));
    }
}

7.将Test.cs脚本拖拽到Main Camera上

8.运行结果

2018-09-02 14:23:02 re_psyche 阅读数 2897
  • Unity3D入门到精通-(4)Unity热更新

    本次系列课程的目标是让Unity3D初学者掌握C语言,Untiy调用lua脚本深入理解uLua框架实现原理,并根据项目的特点实现相应的热更新方案。 适合对象:Unity初学开发者,Unity中级开发者,所有对游戏开发有兴趣的人员。 学习条件:有一定的Unity3D基础,了解C#的基本开发知识。

    2214 人正在学习 去看看 张刚

大多数游戏都是使用Unity 3D做的,我们逆向分析主要分析\assets\bin\Data\Managed中的Assembly-CSharp.dll文件,当我们用.NET Reflector 或者dnSpy 打开Assembly-CSharp.dll文件时,有时候会发现什么信息也看不到,在前面呢有一个红色的x证明被加密了。但是我们在模拟器或者手机上运行时,却能完美运行。

这里写图片描述

也就是说,我们也可以在apk运行时使用一些修改器,将他的内存提取出来,然后再用工具(get_dll_from_dumped_bin.exe)将这些bin文件转换为dll文件。(发现已经有人把工具上传了,就不挂链接了)。我找到了一款被加密U3D游戏,我们在他正常运行时,用gameguardian(https://gameguardian.net/download)导出他的内存。
这里写图片描述

导出以后,我们把这些bin文件传到PC上,然后将这些bin文件与get_dll_from_dumped_bin.exe放在同一目录下,运行get_dll_from_dumped_bin.exe后,即可自动转化。

这里写图片描述

这里会看到有9个dll文件,这个时候我们就可以将文件大小与我们在apk中的dll文件作对比,即可找到正确的dll文件。

2013-10-14 13:56:38 jukai7 阅读数 2786
  • Unity3D入门到精通-(4)Unity热更新

    本次系列课程的目标是让Unity3D初学者掌握C语言,Untiy调用lua脚本深入理解uLua框架实现原理,并根据项目的特点实现相应的热更新方案。 适合对象:Unity初学开发者,Unity中级开发者,所有对游戏开发有兴趣的人员。 学习条件:有一定的Unity3D基础,了解C#的基本开发知识。

    2214 人正在学习 去看看 张刚

      在Unity3D的项目开发过程中,可能会使用一些以前别人写好的C#,Java,C++等文件,我们可以把它们做成动态链接库DLL文件在项目中进行调用,下面用一个简单的例子来说明怎么制作一个DLL文件并在Unity中使用它。

     首先新建一个Unity3D工程,我们命名为"Test",创建一个C#文件,命名为“DLLTest”,文件代码如下所示:

using UnityEngine;
using System.Collections;

public class DLLTest : MonoBehaviour {

	// Use this for initialization
	void Start () {
	
	}
	
	// Update is called once per frame
	void Update () {
	
	}
	
	public void Fun()
	{
		print("Hello World !");
	}
}

      我们只是添加了一个公有函数“Fun”,函数的功能是输出“Hello World !”,然后我们把“DLLTest”文件复制出来,先暂时放在桌面即可。这时可以把Unity3D工程中的“DLLTest”文件删掉了。

      接下来在MonoDevelope中新建一个Solution,即重新建立一个解决方案,命名为“MyTest”,并放在D盘的Projects文件夹,方便以后找到它。步骤是点击File-->New-->solution..,选择C#选项卡下面的Library,然后填写项目名称和选择存放地址,如下图所示:

     

     点击OK,然后把我们之前放在桌面上的“DLLTest”C#文件导入到Solution中,首先确定右上角选择的是“Default”,,然后右键项目名“MyTest”,选择Add-->Add Files..,选择桌面的“DLLTest”即可,它会提示你以怎样的形式导入文件,移动还是复制,随便选一个即可。

      我们Build编译一下,有可能会出现一个.Net框架版本不符合的错误提示(如果没有这个错误则略过)。

         

     解决的方法也很简单,我们选中"MyTest"项目,点击菜单栏的“Project-->MyTest Options”,在弹出的对话框中选择General选项卡,在“Target framework”选项栏中选择合适的版本即可,我这里选择的是4.0,如下图所示:


       再Build编译一次,发现还是有错误,提示缺少命名空间,错误信息如下:


       这是因为缺少“UnityEngine.dll”文件,我们只需把它导入即可,那么这个文件在哪呢?我们把新建的Unity3D项目保存后导出为.exe文件,命名为“temp”那么在导出后的temp_Data文件夹下的Managed文件夹内就可以找到“UnityEngine.dll”文件。

       回到我们在MonoDevelop创建的"MyTest"项目,右键“References“文件夹,选择Edit References,然后选择.Net Assembly选项卡,找到刚刚的”UnityEngine.dll“文件并添加即可。如图所示:


      再Build一次,错误提示就没有了。然后我们打开项目地址,由于我之前保存在D盘的Projects文件夹,所以我的DLL文件存放目录是”D:\Projects\MyTest\MyTest\bin\Debug“,找到”MyTest.dll“并复制出   来,暂时放在桌面。

      打开Unity3D项目”Test“,把”MyTest.dll“文件拖到项目中,然后将MyTest下的DLLTest脚本组件拖到摄像机上,新建一个C#文件,拖到摄像机上,编写代码如下:

using UnityEngine;
using System.Collections;

public class NewBehaviourScript : MonoBehaviour {

	// Use this for initialization
	void Start () {
	
		//获得DLLTest组件
		DLLTest dll = camera.GetComponent<DLLTest>();
		//调用Fun函数
		dll.Fun();
		
	}
	
	// Update is called once per frame
	void Update () {
	
	}
}

     运行,控制台打出”Hello World !“,则调用DLL文件成功。

2018-10-10 15:40:27 omnispace 阅读数 244
  • Unity3D入门到精通-(4)Unity热更新

    本次系列课程的目标是让Unity3D初学者掌握C语言,Untiy调用lua脚本深入理解uLua框架实现原理,并根据项目的特点实现相应的热更新方案。 适合对象:Unity初学开发者,Unity中级开发者,所有对游戏开发有兴趣的人员。 学习条件:有一定的Unity3D基础,了解C#的基本开发知识。

    2214 人正在学习 去看看 张刚

  网上已经有很多文章讲解过Unity3D脚本DLL 解密,基本方法原理都差不多,就是通过HOOK或者调试下断mono_image_open_from_data_with_name这个函数拿到解密后的DLL。这些方法都需要比较专业的知识(Hook或者过反调试),另外对于使用了强度比较高的第三方保护,也无能为力。下面我们来探讨一下更为通用简便的U3D脚本DLL解密方法。

一、脚本解密原理

      先来理一下通过mono_image_open_from_data_with_name解密脚本DLL的原理。

      Unity3d游戏的脚本默认情况下是非加密形态,保存于如下图的压缩包目录下,一般情况名字为Assembly-CSharp.dll或Assembly-CSharp-firstpass.dll。

image.png

       这样的脚本DLL是由C#语言编写的,可以被类似Reflector之类的工具反编译成源码。破解者得到源码就可以对其进行修改,实现内购破解、修改游戏人物属性(修改金币、HP等)、修改战斗技能(无敌、强制胜利等)。        

       我们来看下mono_image_open_from_data_with_name这个函数的源码

image.png

几个主要函数的参数意义:

data: 脚本内容

data_len:脚本长度

name:脚本名称

这个函数执行了如下几步操作:

1.把data指向的脚本拷贝到新申请的内存

2.填充一个MonoImage结构体

3.使用do_mono_image_load初步加载该脚本

4.注册并返回MonoImage结构体指针

    这个原始mono_image_open_from_data_with_name函数,如果输入的data指向的是个加密过的脚本DLL,在这个函数执行之前,先要对该data指向的内存进行解密。

调试解密可在return处下断,此时data指向的内存已经解密,把该处内存拷贝出来即是解密后的DLL

     HOOK解密原理是待mono_image_open_from_data_with_name原始函数返回后拷贝data指向的内存

    不过往往第三方保护都是带反调试的,新手要过掉反调试还是比较费周折的,有些厉害的反调试,老手都不一定过得了。对SO做HOOK也是要有一定的技术基础才能做到。

    对于强度稍微高点的保护,这个函数处是拿不到解密后的DLL的,等这个函数返回的时候,内存可能是空的。  

    绝大部分加密,都紧盯着data指向的内存来做文章,认为只要把mono_image_open_from_data_with_name参数里的data加密搞定就万事大吉了,而忽略掉了另一块永久存在的处于解密状态的DLL内存。

    mono_image_open_from_data_with_name第一步就是把data内存使用memcpy拷贝到一块新申请的内存里,这块内存将永久存在,因为C#语言是需要动态解析类、函数等信息的,时不时要用到这块内存。而data待这个函数调用完后,将会被释放掉。

    这也是某些强度比较高的第三方保护,通过那两种方法都得不到解密DLL的关键所在,它在mono_image_open_from_data_with_name函数返回前就把data指向的内存给清空了。

二、脚本解密方法

    下面就来讲一下我们的U3D脚本DLL解密方法,原理即是利用了内存中存在的那个memcpy拷贝的DLL。

    由于内存中永久存在这个DLL,我们只要全内存搜索这个DLL就可以了。如何全内存搜索呢,自己写个工具还是挺麻烦的一件事。

    其实用手游分析者比较熟悉的一个现成工具就可以做到:烧饼修改器(八门神器等其它修改器也可以),烧饼修改器可以对游戏进行数值搜索。那我们要搜索什么数值呢?

   先用二进制工具hiew来看下Assembly-CSharp.dll

image.png

     这是个PE结构的文件,以4d 5a 90 00这四个字节为magic head,这可以做为C# DLL脚本的特征,我们只要搜索0x905a4d这个数值就可以了,由于烧饼修改器是使用10进制数值,将其转换成10进制值:9460301

    下图是使用烧饼修改器搜索一个3D卡车小游戏的示例:  

image.png

    显示出来的是搜索结果,一共搜索到了12个结果

    第一个搜索结果,即是以52c4c008起始地址的脚本DLL,我们可以使用memdump工具把这块内存DUMP下来,由于不知道大小,先dump 0×3000大小来看一下。

image.png

    保存为abc.dll,再用hiew 看下abc.dll 

image.png

    可以看到DUMP下来的确实是个CSharp 脚本DLL,DLL实际大小可通过下图这个FFI 工具,解析出来,将第 3行的0x10A00 加上 0×200 即是实际大小。可通过这个方法来确定这12 个 DLL ,哪个是你需要解密的 DLL 。 

image.png

    这个方法的优点是可以不用去管反调试,也不用去学习怎么HOOK,用现成的工具组合起来就可以做到解密脚本。最重要一点是对保护强度比较高的保护也可以无视。

http://www.freebuf.com/column/153720.html

2017-06-30 17:12:05 Maxdong24 阅读数 477
  • Unity3D入门到精通-(4)Unity热更新

    本次系列课程的目标是让Unity3D初学者掌握C语言,Untiy调用lua脚本深入理解uLua框架实现原理,并根据项目的特点实现相应的热更新方案。 适合对象:Unity初学开发者,Unity中级开发者,所有对游戏开发有兴趣的人员。 学习条件:有一定的Unity3D基础,了解C#的基本开发知识。

    2214 人正在学习 去看看 张刚

加密原理

Unity3D 是基于 Mono的,我们平时写的 C# 脚本都被编译到了 Assembly-CSharp.dll ,然后 再由 Mono 来加载、解析、然后执行。
Mono 加载 Assembly-CSharp.dll 的时候就是读取文件到内存中,和平时读取一个 游戏资源 文件没什么区别。
为了防止别人破解,我们会对游戏资源加密,简单点的 比如修改文件的一个字节 或者 位移一下 。只要简单的修改一下,破坏原来的文件数据结构,别人就不能用通用的读取工具来读取了。
Mono 读取 Assembly-CSharp.dll 也是如此,我们只要简单的 修改 Assembly-CSharp.dll 的一个字节,就能破坏掉 Assembly-CSharp.dll 的数据结构,然后 Assembly-CSharp.dll 就不再是一个 dll 了,就变成了一个普通的文件,一个系统都不认识的未知类型的文件。

注解:
Mono是一个由Xamarin公司(先前是Novell,最早为Ximian)所主持的自由开放源代码项目。该项目的目标是创建一系列匹配ECMA标准(Ecma-334和Ecma-335)的.NET工具,包括C#编译器和通用语言架构。与微软的.NET Framework(共通语言运行平台)不同,Mono项目不仅可以运行于Windows系统上,还可以运行于Linux,FreeBSD,Unix,OS X和Solaris,甚至一些游戏平台,例如:Playstation 3,Wii或XBox 360。

Assets\bin\Data\Managed\Assembly-CSharp.dll

1)在 Android 中,由 libmono.so 来加载 Assembly-CSharp.dll 。
2)libmono.so 这就是 Mono 了 。

assets\libs\armeabi-v7a
libmono.so
libunity.so

既然 Assembly-CSharp.dll 被我们加密了,那 libmono.so 这个通用的读取工具就不能再 读取已经加密的 Assembly-CSharp.dll 了,所以我们也要修改 重新编译 libmono.so ,给它加上解密函数才行。

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

Mono项目地址:

加密DLL首先要找准unity版本对应的mono;
项目托管在 Github 上,项目地址: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 这两个版本我已经测试通过。别的版本希望大家都能来参与测试。

这里写图片描述

下面我都用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。

    if(name != NULL)
    {
        if(strstr(name,"Assembly-CSharp.dll")){
                       data[0]-=1
        }
    }

注意:
如果你要热更DLL时一定要注意!!这里一定要先判断一下name是否为NULL 不然使用System.Reflection.Assembly.Load 在Android平台反射调用DLL的时候unity 会挂的。
这里写图片描述

还有如果想在 mono里打印Log的话可以使用

#include <glib.h>

g_message(“momo: %s”,str);

OK 然后开始编译mono吧。arm 和x86 两个大概 5 分钟左右就能编译完成。对应会会放在mono根目录build的文件夹里。然后回到生成的adnroid工程中,把libmono.so 分别放在x86和armeabi-v7a文件夹下。

这里写图片描述

再说说自动化的问题,DLL每次代码变更都会重新生成一个新的,那么我总不能每次都手动加密DLL然后在手动的拷贝到assets下面吧。。

再说一句,我的项目在处理自动化打包时用的是adnroid的ant打包。也就是先把unity导出成一个android 工程。然后在打包。所以我的自动化就可以是当android工程生成后,然后把dll读取到内存里,加密后在重新写到原来工程的位置上。如果有朋友不太懂自动化,可以在我博客里搜索一下,以前我有写过。

http://www.xuanyusong.com/archives/3384 环境变量如果你不会加的话,也可以看我这篇文章。

这段代码的意思就是当eclipse的android工程生成后,紧接着就给dll加密。。字节一变那么Dll其实就变成了一个普通的二进制文件。这样用各种反编译Dll的工具就都打不开了。

    //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)){
            //先读取没有加密的dllbyte[] bytes = File.ReadAllBytes (inpath);  

            //字节偏移 DLL就加密了。
            bytes [0] += 1;
            //在写到原本的位置上

            File.WriteAllBytes (inpath, bytes);

        }

    }

然后还有前面我们编译出来的两个 mono.so 也要在这里自动化一并拷贝到这个工程对应的目录下面(可以在shell里拷贝,也可以在C#里拷贝)。 接下来就调用自动打包apk就行了。。总之最后的效果就是Dll不能被解开了。如下图所示。
这里写图片描述
这里写图片描述

unity dll实现热更新

阅读数 2493

Unity中调用DLL库

阅读数 168

没有更多推荐了,返回首页