基于Mono的Unity编辑器加密方案

人盡茶涼 提交于 2019-11-29 19:27:01

引言

该博客是写给需要保护自己知识产权的游戏开发者,本文加密方式相对简单易懂,但起到的效果足以让大多数小白窃取者望而却步

Windows加密篇

基于Unity2017

一.准备

1.Mono源码(根据自己的Unity版本下载相应源码): https://github.com/Unity-Technologies/mono
2.Visual Studio 2010(2017的Mono版本需要用此VS版本进行编译)
3.加密的算法(随意,基于C编码)

二.正文

在这里插入图片描述

1.修改载入源码(前几部分比较简单,不再一一赘述,直接进入主题)
  • 原理:C#是可以动态加载DLL(Assembly.LoadFile), 我们主要是在这里做文章
    • 通过阅读mono源码后我们可以知道
    • 对DLL解析代码为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 (!data || !data_len) {
		if (status)
			*status = MONO_IMAGE_IMAGE_INVALID;
		return NULL;
	}

	// 这里的data为读取DLL后的数据
	// 我们可以在这里通过对名字的筛选来对我们已加密的DLL进行解密

	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);
	}

	image = 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);
}
  • 读取DLL代码为assembly.c的mono_assembly_open_full
MonoAssembly *
mono_assembly_open_full (const char *filename, MonoImageOpenStatus *status, gboolean refonly)
{
	MonoImage *image;
	MonoAssembly *ass;
	MonoImageOpenStatus def_status;
	gchar *fname;
	gchar *new_fname;
	
	g_return_val_if_fail (filename != NULL, NULL);

	if (!status)
		status = &def_status;
	*status = MONO_IMAGE_OK;

	if (strncmp (filename, "file://", 7) == 0) {
		GError *error = NULL;
		gchar *uri = (gchar *) filename;
		gchar *tmpuri;

		/*
		 * MS allows file://c:/... and fails on file://localhost/c:/... 
		 * They also throw an IndexOutOfRangeException if "file://"
		 */
		if (uri [7] != '/')
			uri = g_strdup_printf ("file:///%s", uri + 7);
	
		tmpuri = uri;
		uri = mono_escape_uri_string (tmpuri);
		fname = g_filename_from_uri (uri, NULL, &error);
		g_free (uri);

		if (tmpuri != filename)
			g_free (tmpuri);

		if (error != NULL) {
			g_warning ("%s\n", error->message);
			g_error_free (error);
			fname = g_strdup (filename);
		}
	} else {
		fname = g_strdup (filename);
	}

	mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
			"Assembly Loader probing location: '%s'.", fname);

	new_fname = NULL;
	if (!mono_assembly_is_in_gac (fname))
		new_fname = mono_make_shadow_copy (fname);
	if (new_fname && new_fname != fname) {
		g_free (fname);
		fname = new_fname;
		mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
			    "Assembly Loader shadow-copied assembly to: '%s'.", fname);
	}
	
	image = NULL;
	// 这里会对读取进来的数据进行分析
	// 当bundles加密后,原来的DLL数据格式被改变
	// 所以我们需要在这里做处理,让读进来的DLL数据不被过滤
	
	FILE * pFile;
	long lSize;
	char * buffer;
	size_t result;
	char*name;
	name = g_path_get_basename(filename); // 获取DLL名称
	if (strstr(name, "MyXXX.dll" != NULL) // 判断是否符合需要解密条件(这里是用DLL的名称)
	{
		pFile = fopen(filename, "rb");
		fseek(pFile, 0, SEEK_END);
		lSize = ftell(pFile);
		rewind(pFile);
		buffer = (char*) malloc (sizeof(char)*lSize);
		result=fread(buffer, 1, lSize, pFile);
		fclose(pFile);
		image = mono_image_open_from_data_with_name(buffer, lSize, FALSE, status, refonly, name);
		free(buffer);
	}
	else if (bundles != NULL)
		image = mono_assembly_open_from_bundle (fname, status, refonly);

	if (!image)
		image = mono_image_open_full (fname, status, refonly);

	if (!image){
		if (*status == MONO_IMAGE_OK)
			*status = MONO_IMAGE_ERROR_ERRNO;
		g_free (fname);
		return NULL;
	}

	if (image->assembly) {
		/* Already loaded by another appdomain */
		mono_assembly_invoke_load_hook (image->assembly);
		mono_image_close (image);
		g_free (fname);
		return image->assembly;
	}

	ass = mono_assembly_load_from_full (image, fname, status, refonly);

	if (ass) {
		mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
				"Assembly Loader loaded assembly from location: '%s'.", filename);
		if (!refonly)
			mono_config_for_assembly (ass->image);
	}

	/* Clear the reference added by mono_image_open */
	mono_image_close (image);
	
	g_free (fname);

	return ass;
}

之后使用VS2010进行编译替换即可生效
2018后,方法有所不同,后面我再找个时间更新下,原理基本还是一样的

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!