• UnityC# MD5验证

    2015-07-03 00:28:43
    孙广东 2014.6.24 数据经网络传输后会变得非常不安全,最简单有效的解决方案是给数据加一个密钥,使用MD5 算法算出校验码,服务器收到数据和校验码后在进行比较校验码是否正确,以此来判断数据是否修改过。...

    孙广东  2014.6.24


         数据经网络传输后会变得非常不安全,最简单有效的解决方案是给数据加一个密钥,使用MD5 算法算出校验码,服务器收到数据和校验码后在进行比较校验码是否正确,以此来判断数据是否修改过。 PHP生成 的 MD5 校验默认为32位的字符串,  而C#默认的是16位的字节数组,需要略加修改,转为32个字节的字符串,代码如下:

    参考地址为:  http://wiki.unity3d.com/index.php?title=MD5

            public static string Md5Sum(string strToEncrypt)
            {
                // 将需要加密的字符串转为byte数组
                byte[] bs = UTF8Encoding.UTF8.GetBytes(strToEncrypt);
    
                // 创建md5 对象
                System.Security.Cryptography.MD5 md5;
                md5 = System.Security.Cryptography.MD5CryptoServiceProvider.Create();
    
                // 生成16位的二进制校验码
                byte[] hashBytes = md5.ComputeHash(bs);
    
                // 转为32位字符串
                string hashString = "";
                for (int i = 0; i < hashBytes.Length; i++)
                {
                    hashString += System.Convert.ToString(hashBytes[i], 16).PadLeft(2, '0');
                }
    
                return hashString.PadLeft(32, '0');
            }


    使用这个 MD5 函数非常简单, 在下面的代码示例中,数据是包含有 “hello world” 的一个字符串, 密钥位123, 使用Md5Sum算出32位的校验码字符串。

                string data = "hello world";
                string key = "123";
                Md5Sum(data + key);  // 返回

    服务器端:


    如上文所指出,上述的unity代码将返回匹配那个从 PHP 的 md5() 函数返回的哈希值。如果在服务器端使用的另一种语言,这里有一些例子:

    Perl

    <perl>use Digest::MD5 qw(md5_hex);

    my $hashString = md5_hex($stringToHash); </perl>

    PHP

    < perl > $hashString = md5($stringToHash);< / perl >

    Python

    <python>import hashlib

    def md5Sum(inputString):

      return hashlib.md5(inputString).hexdigest()
    

    < / python >

    红宝石

    require 'digest/md5'

    def md5Sum(inputString)


       Digest::MD5.hexdigest(inputString)
    

    end

    Shell

    你必须安装在服务器上的 md5sum 程序。 <bash>HASH = `echo "$STRING_TO_HASH" | md5sum | cut -f 1 -d' '` </bash>


    在来看看在Unity中的其他应用:

     为库存系统生成 md5 的哈希值。使用哈希值我有几个重要原因。

    1) 库存系统被链接到 PlayerPrefs。我需要一个非整数的方式来表示这个item 。

    2) 我想要使用某种形式的哈希值来表示该item ,因为事实证明很难有人通过修改 PlayerPrefs作弊。

    3) PlayerPrefs 键也是 md5 的哈希值,使得阅读 PlayerPrefs 文件是不可能。

    using UnityEditor;
    using UnityEngine;
     
    using System.Security.Cryptography;
    using System.Text;
     
    public class ItemGUID : EditorWindow
    {
    	// Add menu item named "My Window" to the Window menu
    	[MenuItem("UnityExtensions/Generate ItemGUID")]
    	public static void ShowWindow()
    	{
    		MD5 md5Hash = MD5.Create();
    		BaseItem[] items = FindSceneObjectsOfType(typeof(BaseItem)) as BaseItem[];
    		foreach (BaseItem item in items)
    		{
    			byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(item.name));
    			StringBuilder sBuilder = new StringBuilder();
    	        	for (int i = 0; i < data.Length; i++)
    	        	{
    		            sBuilder.Append(data[i].ToString("x2"));
    		        }
     
    			item.guid = sBuilder.ToString();
    		}
    	}
    }

    对我而言,我有许多游戏对象,表示我的游戏中的Items 。每个游戏对象都有 BaseItem 组件,其中包含items 统计的一些实用函数。

    它有一个public 字符串 guid、 变量,每次我运行此编辑器脚本设置他。在创建新items时,我重新运行该脚本,将所有的 guid 变量重置为其名称的 md5 哈希值。

    非常简单和快速,和最重要的是超级有效。



    
    
    展开全文
  • Unity3d项目中获取APK包签名公钥的方法,核心思想就是通过JNI调用Android提供的方法。不过Unity3d提供了比JNI更上一层的类AndroidJavaObject以及继承它的AndroidJavaClass,帮助开发者省去很多工作。这两种类型的...

    在Unity3d项目中获取APK包签名公钥的方法,核心思想就是通过JNI调用Android提供的方法。不过Unity3d提供了比JNI更上一层的类AndroidJavaObject以及继承它的AndroidJavaClass,帮助开发者省去很多工作。这两种类型的函数及内部原理可以看Unity3d官网,也可以反编译看里面的代码,直接说用法。

    首先要获取到当前运行程序的Activity才好继续以后的操作。Unity3d在程序启动时记录下了这个值,可以通过UnityPlayer类的静态字段currentActivity来取得,UnityPlayer是由UnityPlayerActivity这个继承自Activity的类在onCreate时创建并传入当前Activity(详细流程可以通过jd-gui或反编译项目smali查看)。取到Activity以后就可以getPackageManager、getPackageInfo来获取到签名。得到的签名据说保存了公钥以及其他一些信息,这个没有具体深入了解,但是可以从签名获取到公钥是一定可以的,CertificateFactory的generateCertificate、getPublicKey方法取得最终值。下面是完整代码,注释对应java代码:

    
        private byte[] GetSignature()
        {
            //Player = new UnityPlayer();
            AndroidJavaClass Player = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
            //Activity = Player.currentActivity;
            AndroidJavaObject Activity = Player.GetStatic<AndroidJavaObject>("currentActivity");
            //PackageManager = Activity.getPackageManager();
            AndroidJavaObject PackageManager = Activity.Call<AndroidJavaObject>("getPackageManager");
            //GET_SIGNATURES = PackageManager.GET_SIGNATURES;
            int GET_SIGNATURES = PackageManager.GetStatic<int>("GET_SIGNATURES");
            //PackageInfo = PackageManager.getPackageInfo("com.ztx.uni", PackageManager.GET_SIGNATURES);
            AndroidJavaObject PackageInfo = PackageManager.Call<AndroidJavaObject>("getPackageInfo", "com.ztx.uni", GET_SIGNATURES);
            //Signatures = PackageInfo.signatures;
            AndroidJavaObject[] Signatures = PackageInfo.Get<AndroidJavaObject[]>("signatures");
            //return Signatures[0].toByteArray();
            return (Signatures != null && Signatures.Length > 0) ? Signatures[0].Call<byte[]>("toByteArray") : null;
        }
    
        private string GetPublicKey()
        {
            byte[] Signatures = GetSignature();
    
            if (Signatures != null)
            {
                //CCertificateFactory = new CertificateFactory();
                AndroidJavaClass CCertificateFactory = new AndroidJavaClass("java.security.cert.CertificateFactory");
                //OCertFactory = CCertificateFactory.getInstance();
                AndroidJavaObject OCertFactory = CCertificateFactory.CallStatic<AndroidJavaObject>("getInstance", "X.509");
                //OByteArrayInputStream = new ByteArrayInputStream(Signatures);
                AndroidJavaObject OByteArrayInputStream = new AndroidJavaObject("java.io.ByteArrayInputStream", Signatures);
                //OX509Certificate = OCertFactory.generateCertificate(OByteArrayInputStream);
                AndroidJavaObject OX509Certificate = OCertFactory.Call<AndroidJavaObject>("generateCertificate", OByteArrayInputStream);
                //OPublicKey = OX509Certificate.getPublicKey();
                AndroidJavaObject OPublicKey = OX509Certificate.Call<AndroidJavaObject>("getPublicKey");
    
                string publickey = OPublicKey.Call<string>("toString");
                AndroidJavaObject CString = new AndroidJavaObject("java/lang/String", publickey);
                int start = CString.Call<int>("indexOf", "modulus");
                int end = CString.Call<int>("indexOf", "public");
    
                if (start >= 0 && end >= 0)
                {
                    return CString.Call<string>("substring", start + 8, end - 1);
                }
            }
            
            return null;
        }
    

    在GetPublicKey()中取得的OPublicKey是如下的形式:

    OpenSSLRSAPublicKey
    {modulus=d6931904dec60b24b1edc762e0d9d8253e3ecd6ceb1de2ff068ca8e8bca8cd6bd3786ea70aa76ce
    60ebb0f993559ffd93e77a943e7e83d4b64b8e4fea2d3e656f1e267a81bbfb230b578c20443be4c7218b846f
    5211586f038a14e89c2be387f8ebecf8fcac3da1ee330c9ea93d0a7c3dc4af350220d50080732e0809717ee6
    a053359e6a694ec2cb3f284a0a466c87a94d83b31093a67372e2f6412c06e6d42f15818dffe0381cc0cd444d
    a6cddc3b82458194801b32564134fbfde98c9287748dbf5676a540d8154c8bbca07b9e247553311c46b9af76
    fdeeccc8e69e7c8a2d08e782620943f99727d3c04fe72991d99df9bae38a0b2177fa31d5b6afee91f,publicExponent=3}

    而我们要的只是其中的256位公钥,所以在函数后面取了modulus和publicExponent之间的值。至此全部工作结束,很简单吧,如果在ndk中做公钥提取也是这个道理,不过要写很多很多代码。。

    展开全文
  • Unity3D实战之文件加密

    2019-07-05 10:08:48
    移动端游戏经常被一些玩家破解成白包,但是为了安全性,开发者还是需要使用算法对文本文件加密,加密的算法非常多,比如通常使用的是MD5算法,OBFS算法,SHA512算法等。由于MD5算法经常使用,网上也有现成的代码本节...

    移动端游戏经常被一些玩家破解成白包,但是为了安全性,开发者还是需要使用算法对文本文件加密,加密的算法非常多,比如通常使用的是MD5算法,OBFS算法,SHA512算法等。由于MD5算法经常使用,网上也有现成的代码本节就直接掠过,直接讲OBFS,SHA512加密算法,为了便于大家理解,先把加密算法代码奉献上,加密函数代码如下所示:

    //OBFS加密算法
        private static string OBFS(string str)
        {
            int length = str.Length;
            var array = new char[length];
            for (int i = 0; i < array.Length; i++)
            {
                char c = str[i];
                var b = (byte)(c ^ length - i);
                var b2 = (byte)((c >> 8) ^ i);
                array[i] = (char)(b2 << 8 | b);
            }
            return new string(array);
        }
    
        //SHA512加密算法
        public static string GetSHA512Password(string password)  
        {  
            byte[] bytes = Encoding.UTF7.GetBytes(password);  
            byte[] result;  
            SHA512 shaM = new SHA512Managed();  
            result = shaM.ComputeHash(bytes);  
            StringBuilder sb = new StringBuilder();  
            foreach (byte num in result)  
            {  
                sb.AppendFormat("{0:x2}", num);  
            }  
            return sb.ToString();  
        }

    以上两个算法实现了文本文件的密码,函数的参数是开发者自己定义的字符串,然后在该字符串的基础上通过算法加密生成新的字符串用于压缩文件加密。下面列出配置文件,并且调用OBFS函数或者是SHA512函数对文件进行加密,返回的是经过加密字符串,同时调用函数SaveConfigXMLTOZip对文件进行压缩加密。对应的函数语句如下所示:

    private const string configurationFile = "config.txt";
        private const string localizationFile = "translations.txt";
        private /*const*/ string configurationZipPwd = OBFS("~Ũɦ;о_ٷݫ࠵ळੰୠ_");
    private /*const*/ string configurationZipPwd = GetSHA512Password("ٷ※▊ぷ┩▓ㄘЖ╔╕Ψ≮≯ゆǘйξζ");
    #if UNITY_EDITOR
        protected void SaveConfigXMLToZip()
        {
            using (ZipFile zipFile = new ZipFile(Encoding.UTF8))
            {
                zipFile.Password = configurationZipPwd;
                zipFile.AddEntry(configurationFile, configuration.bytes);
                zipFile.AddEntry(localizationFile, localization.bytes);
                string zipPath = Path.Combine(Application.persistentDataPath, configurationZipFile);
                LogTool.Log("Saving configuration in \"" + zipPath + "\"");
                zipFile.Save(zipPath);
            }
        }
    #endif

    文件的压缩是在编辑模式下,程序运行时会将文本文件压缩同时把加密的密码赋值给它,在程序启动时,先从资源服务器加载文件的版本号,通过与本地的版本号对比决定下载需要的文本文件。函数代码如下所示:

    #region Coroutines
        IEnumerator DownloadVersionFile()
        {
            Asserts.Assert(!downloadingVersionFile);
            downloadingVersionFile = true;
    
            WWW versionLoader = new WWW(configurationZipURL + versionFile + "?nocache=" + Environment.TickCount);
    
            while (!versionLoader.isDone)
            {
                yield return new WaitForEndOfFrame();
            }
    
            if (versionLoader.isDone && string.IsNullOrEmpty(versionLoader.error))
            {
                versionString = versionLoader.text;
            }
            else
                versionString = version.text;
    
            versionLoader.Dispose();
    
            LogTool.Log("VERSION NUMBER: " + versionString);
    
                downloadingVersionFile = false;
    
            PlayerPrefs.SetInt("last_vn", lastVersionNumber);
        }
    

    它的下载方式是通过WWW下载的,先从资源服务器下载版本的文本文件,接下来下载文件的压缩包,函数代码如下所示:

     IEnumerator DownloadZip()
        {
            Asserts.Assert(!downloadingZip);
            downloadingZip = true;
            WWW zipLoader = new WWW(configurationZipURL + configurationZipFile + "?nocache=" + Environment.TickCount);
    
            while (!zipLoader.isDone)
            {
                if (stopDownloading)
                {
                    downloadingZip = false;
                    stopDownloading = false;
    
                    LogTool.Log("Download configuration STOPPED!");
                }
    
                yield return new WaitForEndOfFrame();
            }
    
            if (zipLoader.isDone && string.IsNullOrEmpty(zipLoader.error))
            {
                LogTool.Log("**** PELLE: DOWNLOADING ZIP COMPLETED! Duration: " + (Time.realtimeSinceStartup - startTime));
                using (FileStream fs = new FileStream(Path.Combine(Application.persistentDataPath, configurationZipFile), FileMode.Create))
                {
                    fs.Seek(0, SeekOrigin.Begin);
                    fs.Write(zipLoader.bytes, 0, zipLoader.bytes.Length);
                    fs.Flush();
                }
    
                zipLoader.Dispose();
    
                if (!downloadingZip)
                {
                    LogTool.Log("Download configuration OK!");
                    yield break;
                }
                else
                    LogTool.Log("Download configuration OK, configurations will be loaded from new zip!");
            }
            else
            {
                zipLoader.Dispose();
                downloadingZip = false;
                stopDownloading = false;
    
                yield break;
            }
    
            if (!dataLoaded && !stopDownloading)
                this.TryLoadingXMLsFromZip();
    
            downloadingZip = false;
            stopDownloading = false;
        }

    最后一步就是解压缩文件并且解释文本文件,函数代码如下所示:

      protected void TryLoadingXMLsFromZip()
        {
            string zipPath = Path.Combine(Application.persistentDataPath, configurationZipFile);
            if (!File.Exists(zipPath))
            {
                LogTool.Log("Configuration not found!");
                this.ParseConfigXML(configuration.text, false);
                this.ParseLocalizationXML(localization.text, false);
                return;
            }
    
            using (ZipFile zipFile = new ZipFile(zipPath, Encoding.UTF8))
            {
                zipFile.Password = configurationZipPwd;
    
                ZipEntry xmlConfEntry   = zipFile[configurationFile],
                         xmlLocaleEntry = zipFile[localizationFile];
                if (null == xmlConfEntry || null == xmlLocaleEntry)
                {
                    LogTool.Log("Downloaded configuration INVALID!");
                    this.ParseConfigXML(configuration.text, false);
                    this.ParseLocalizationXML(localization.text, false);
                    return;
                }
    
                using (MemoryStream ms = new MemoryStream())
                {
                    xmlConfEntry.Extract(ms);
    
                    string xmlText = Encoding.UTF8.GetString(ms.GetBuffer(), 0, ms.GetBuffer().Length);
                    this.ParseConfigXML(xmlText, true);
    
                    ms.Seek(0, SeekOrigin.Begin);
                    xmlLocaleEntry.Extract(ms);
    
                    xmlText = Encoding.UTF8.GetString(ms.GetBuffer(), 0, ms.GetBuffer().Length);
                    this.ParseLocalizationXML(xmlText, true);
                }
            }
        }

    这样整个加密流程就给读者介绍完了,后面将脚本挂接到对象上,在程序运行时做初始化操作处理就可以了,详情可以查看笔者已出版的著作:《Unity3D实战核心技术详解》一书。

    笔者简介:姜雪伟个人网页

    展开全文
  • Unity3D机器学习之环境搭建。原文链接: https://unity3d.college/2017/10/25/machine-learning-in-unity3d

    前言

    赋闲在家,在Unity3D官方论坛上看到“机器学习挑战赛”后琢磨着学习学习。然后在网上看到一篇难得的指导详尽、新手友好的环境搭建教程,可惜是英文的,想着以后可能还会参考,还是读中文比较快,所以就自己简译一次放在这里,也供他人参考。

    原文链接: https://unity3d.college/2017/10/25/machine-learning-in-unity3d-setting-up-the-environment-tensorflow-for-agentml-on-windows-10/

    软件环境

    操作系统:Win10 x64
    Unity3D: 2017.2.0f3

    正文

    环境搭建

    1.安装CUDA Toolkit

    下载CUDA Toolkit 8.0 GA2,链接地址:https://developer.nvidia.com/cuda-toolkit-archive

    这里写图片描述

    下载完毕后运行并安装,一路默认next即可,不过需要注意的是安装前应确保Unity3D Editor 和 Visual Studio没有实例在运行。

    2.安装CUDNN

    CUDNN:CUDA Deep Neural Network library
    下载链接:https://developer.nvidia.com/cudnn
    这里下载需要注册Nvidia的账号,注册一个就好了,免费的。之后选择版本“v6.0 for CUDA 8.0”

    这里写图片描述

    下载的是zip压缩文件,下载后将其中的bin、include、lib三个文件夹拷贝到CUDA的文件目录下,一般是:C:\Program Files\NVIDIA GPU Computing Toolkit\cuda\v8.0

    这里写图片描述

    3.配置环境变量

    • 在系统变量(注意不是用户变量)中新增CUDA_HOME变量,值为CUDA安装目录,一般是:C:\Program Files\NVIDIA GPU Computing Toolkit\cuda\v8.0
    • 编辑系统变量PATH,新增两个路径:
      C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v8.0\lib\x64
      C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v8.0\extras\CUPTI\libx64

    这里写图片描述

    4.安装Anaconda

    这个是用于配置Python环境,下载链接:https://www.anaconda.com/download/

    这里写图片描述

    下载后一路默认安装即可。安装完毕后开始菜单栏中搜索Anaconda Prompt并点击运行

    这里写图片描述

    接下来,搭建并安装tensorflow环境,依次输入以下命令:

    > conda create -n tensorflow-gpu python=3.5.2
    > activate tensorflow-gpu
    > pip install tensorflow-gpu
    > python
    > import tensorflow as tf

    5.下载样例工程

    下载地址:https://github.com/Unity-Technologies/ml-agents

    “以管理员身份运行”另一个Anaconda Prompt窗口,命令行进入工程文件下的python目录,比如我的是:D:\program study\ML-Agent\ml-agents-master\python

    运行如下命令以安装html5lib库

    > conda install –f html5lib
    > pip install .

    注意别漏了那个点,表示当前目录
    如果上述步骤都没有出错,那么环境搭建部分就算完成了,接下来进入机器学习部分。

    机器学习

    这里我选择了平衡球项目,详细步骤可在下载下来的样例工程目录中的“docs/Getting-Started-with-Balance-Ball.md”文件里查看。

    1.编译工程

    • 打开Unity3D样例工程“unity-environment”
    • 选择3DBall场景,目录为Assets/ML-Agents/Examples/3DBall/
    • PlayerSettings中勾选Resolution and Presentation -> Run in Background
    • PlayerSettings中Resolution and Presentation -> Display Resolution Dialog设置为Disable
    • 定位到Hierarchy面板中Ball3DAcademy下Ball3DBrain物体上,将Brain脚本下的Brain Type设置为“External”
    • 编译exe文件,并将输出目录设置为样例工程的python目录,例如我的输出文件名为BalanceBall,目录结构如下图

    这里写图片描述

    2.强化训练

    测试Python API

    在Anaconda Prompt窗口下,切换路径到python目录下后,输入命令

    > jupyter notebook

    然后用浏览器打开http://localhost:8888,可以看到如下页面

    这里写图片描述

    使用PPO算法训练

    点击上述页面(http://localhost:8888)中的ppo.ipynb,在新开窗口中修改如下配置:
    - env_name设置为你的输出exe文件名
    - max_steps设置为50000
    - buffer_size设置为5000
    - batch_size设置为512
    其中后三个修改是为了加快学习速度,也可以保持默认不变,或者修改为其他值。
    设置完后,鼠标点击页面最下方的“Export the trained Tensorflow graph”模块,然后点击菜单栏”Cell”->”Run All Above”,稍等片刻后,即会自动运行输出的exe程序开始训练,训练完毕后会在python/models/ppo目录下生成对应的.bytes文件并自动关闭exe程序窗口。

    这里写图片描述

    这里写图片描述

    (可选)观察训练过程

    在cmd窗口下进入python目录,运行以下命令后,用浏览器打开localhost:6006即可观察训练过程(不过我使用时页面是空白的)

    > tensorboard --logdir=summaries

    具体参数含义可见docs下的Getting-Started-with-Balance-Ball.md文档,这里就不写了。

    设置TensorFlowSharp支持

    这个需要下载TFSharpPlugin.unitypackage插件,下载链接:https://s3.amazonaws.com/unity-agents/0.2/TFSharpPlugin.unitypackage
    下载速度可能比较慢,可以网上搜下有没有相应资源,我在网上搜了一个,但是下载后无法导入Unity,报错无法解压缩,后来再用原链接下,很快又下好了,不知道为啥。

    下载完毕后,导入到Unity工程中,这个包中包含了样例工程的文件和我们需要的Plugins下的文件,因为导入时不能选择特定文件,所以全部导入覆盖即可,完成后会发现Plugins目录下相较之前多了Android、IOS、Computer三个文件夹和System.Numerics及System.ValueTuple两个文件。

    然后在PlayerSettings中修改如下两个设置:
    - Other Settings下Scripting Runtime Version设置为Experimental (.NET 4.6 Equivalent)
    - Other Settings下Scripting Defined Symbols添加ENABLE_TENSORFLOW后切记回车

    点击File -> Save Project后重启Unity3D Editor。

    注意: TensorFlowSharp支持仍为实验功能,启用支持后,Brain Type只能选择”Internal”!

    导入训练后的模型
    • 将之前训练生成的.bytes文件拷贝至unity-environment/Assets/ML-Agents/Examples/3DBall/TFModels/目录
    • Unity编辑器中修改Ball3DAcademy下Ball3DBrain物体上Brain脚本的Brain Type为“Internal”,Graph Model设置为导入的.bytes文件,Graph Placeholder设置为1,增加一个epsilon,其属性为Name:epsilon,ValueType:FloatingPoint,MinValue:0,MaxValue:0

    结果

    以上设置完毕后,点击运行,即可看到球体掉落在平板上后已可维持平衡而不是之前从平板上滑落。

    这里写图片描述

    展开全文
  • Sqlite的应用场景 在判断是否使用存储格式为Sqlite模式的标准,我们的标准是内容只读。也就是说,除非发布者修改Sqlite内容,玩家...对于如何使用Sqlite,请参考我的另外一篇文章《Unity本地数据存储---Sqlite和JSON

    Sqlite的应用场景

    在判断是否使用存储格式为Sqlite模式的标准,我们的标准是内容只读。也就是说,除非发布者修改Sqlite内容,玩家只有读取的权限。

    换个角度说,Sqlite里面的数据都是游戏基础配置数据,比如游戏的关卡(不包括玩家取得的成就)、怪物的血量、装备的模板数据

    对于如何使用Sqlite,请参考我的另外一篇文章《Unity本地数据存储---Sqlite和JSON》

    问题重现

    OK,既然我们选择了Sqlite作为存储手段之一,那么我们也嘚重视起安全性。

    以android而言,我们发布个apk文件,修改后缀为rar,解压,打开文件夹,sqlite数据文件默认就在assets目录下。

    其他人拿个sqlite管理工具,改改内容,比如新手乌木剑属性模板,攻击本来是1~5,改成99999~99999;OK,再重新压缩一个包,修改后缀为.apk。

    嘚,破解版就这么出来了。

    解决之道

    保证数据安全性,我们第一个想到的就是加密数据内容

    但对于使用sqlite保存配置数据,反而觉得对比MD5值更合适。

    加密内容已经违背了使用sqlite(通用、简单)的初衷,因此我们选择所有的“配置数据”都用明文存储在本地,但是只要你篡改了数据,我通过MD5对比就可以侦测到。

    加密算法,更多的是在保存玩家的核心数据上,比如将序列化后的json字符串加密,写入到本地文件。

     

    OK,言归正传,讲下思路:

    1:每次发布新版本的时候,重新计算下db文件的MD5,将其保存在某个位置

    简单: 将计算出来的MD5值,赋值到某个常量

    远程:将版本号和MD5值作为键值对,保存在远程服务端。用户发送一个版本号,服务器返回一串对应版本的MD5字符串。远程的话版本号是必须的。不然老的版本MD5值和新版本永远对不上,就废了。。

    2:玩家运行程序的时候,计算db文件的得出MD5,和我们第一步保存的MD5做对比,如果一致,则运行游戏;否则认定db文件被篡改过的,你可以搭建一个远程db下载。也可以粗暴的提示用户,文件格式损害,然后给出个下载链接。

     

    因为本人实际项目中的db文件很小(还没做装备属性模板,预计最多也就百来K吧),所以计算MD5值不会耗费多少资源

     

    奉上MD5计算类

    复制代码
    using System.IO;
    using System.Security.Cryptography;
    using System.Text;
    
    /// <summary>
    /// 文件MD5计算器
    /// </summary>
    public class FileMD5Helper
    {
        #region 公共方法
    
        /// <summary>
        /// 计算文件的md5值,返回大写格式
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public static string GenerateFileMD5Upper(string url)
        {
            if (File.Exists(url) == false)
                return string.Empty;
    
            byte[] fileByte = File.ReadAllBytes(url);
    
            if (fileByte == null)
                return string.Empty;
    
            byte[] hashByte = new MD5CryptoServiceProvider().ComputeHash(fileByte);
    
            return byteArrayToString(hashByte);
        }
    
        #endregion
    
        #region 私有方法
    
        /// <summary>
        /// 输出数据的十六进制字符串
        /// </summary>
        /// <param name="arrInput"></param>
        /// <returns></returns>
        private static string byteArrayToString(byte[] arrInput)
        {
            StringBuilder sOutput = new StringBuilder(arrInput.Length);
    
            for (int i = 0; i < arrInput.Length; i++)
            {
                sOutput.Append(arrInput[i].ToString("X2"));
            }
            return sOutput.ToString();
        }
    
        #endregion
    }
    复制代码

     

    最后的话:

    围绕着本地数据安全,我们一共有两项措施

    1:我们通过比较md5,来确保游戏配置db数据没被篡改。

    2:我们通过 SystemInfo.deviceUniqueIdentifier,来确保玩家之间的存档不会通用。

    说实话,我不是很喜欢花费时间在安全性上,如何让游戏变得好玩,才是重点

    抛砖引玉,欢迎各位朋友指教

    展开全文
  • Unity编辑器小工具——文件查重(MD5算法思想: 在Unity中,每一个不同资源、文件所生成MD5码是不同的,但是相同文件,路径不同、文件名不同的同一类文件的MD5码是相同的,所以可以通过生成每个文件的MD5来判断...
  • 很多刚刚接触Unity3d的童鞋花了大量的时间自学,可总是把握不好Unity3d的烘焙,刚从一个坑里爬出来,又陷入另一个新的坑,每次烘焙一个场景少则几个小时,多则几十个小时,机器总是处于假死机状态,半天看不到结果,...
  • 本课程是 Unity 3D 系列教程,目标是带领读者搭建一个商业游戏的网络架构设计,该架构设计是游戏的核心技术,将采用 Unity 2017.2 最新版本作为开发工具。内容分为 UI 架构、技能架构、服务器和网络同步四大部分,共...
  • 在我们的Unity3D游戏开发过程中,我做了一些防外挂与防破解工作,主要是随机数校验,内存加密和C#脚本加密。
  • unity3d广东麻将算法

    2017-05-24 15:46:32
    欢迎一起学习交流 QQ:51267195 电话:18816791911 微信同号 package com.linyun.xgame.service.impl; import java.util.ArrayList; import java.util.Date; import java.util.HashMap;...impo
  • 此处共有三段加密算法程序,分别是OBFS、SHA512和MD5,参考姜雪伟的《Unity3D 实战核心技术详解》一书中的代码,此处和大家分享。 //OBFS加密算法 private static string OBFS(string str) { int length = str...
  • MD5验证

    2018-10-23 16:47:13
    首先要给数据加一个密匙,然后使用MD5算法算出校验码,服务器收到客户端的数据后,使用密匙再次进行MD5计算得出新的校验码,在与客户端发来的校验码进行对比,若果一致,说明数据是合法的,否则可能被修改过。...
  • 解密无非就为了 修改游戏功能数据、提取游戏资源、加入自己想加的广告... 加密就是保护游戏不被恶意修改,经常...先说下关于Unity C#代码部分的加密(Android和IOS有时间再细说),很多人说混淆,虽然有几个混淆插件Cod
  • HMAC-SHA1 算法 //HMAC-SHA1 算法1 public static object HMAC_SHA1(string content, string secretKey, bool raw_outut = false) { Encoding enc = Encoding.UTF8; HMACSHA1 hmacsha1 = new HMACSHA1(enc.Ge...
  • Unity3D_Util_加密

    2019-07-04 19:16:20
    Unity开发中常用的加密算法 MD5,Sha1、CRC32,Des,Res等的实现 public class EncryptUtil { #region MD5 public static string MD5Encrypt(string str, int length = 32) { if (...
  • 废话少说,直接上代码。 MD5: public static string GenerateMD5(string txt) //MD5 ... using (MD5 mi = MD5.Create()) { byte[] buffer = Encoding.Default.GetBytes(txt); byte[] newBuffer = m
  • Unity3D动态下载资源,有没有解?有,AssetBundle就是通用解,任何一本书都会花大幅篇章来介绍AssetBundle. 我们也来说说AssetBundle 我们试全面的分析一下Unity3D提供的资源加载机制 1.Resources//内嵌资源...
  • 官方介绍:https://docs.unity3d.com/Manual/AssetBundlesIntro.html AssetBundle是一个压缩包包含模型、贴图、预制体、声音、甚至整个场景,可以在游戏运行时被加载(热更); AssetBundle自身保存着互相的依赖...
  • Unity3d数据加密

    2019-08-01 08:19:40
    unity中能够使用c#自带的对称算法对数据进行加密,以下两种加密算法: using System; using System.Text; using System.Security.Cryptography; using System.IO; using UnityEngine; public class ...
1 2 3 4 5 ... 20
收藏数 406
精华内容 162
热门标签