【Unity3D】apk加密(global-metadata.dat加密)

news2025/1/11 4:50:05

涉及:apk、aab、global-metadata.dat、jks密钥文件、APKTool、zipalign

使用7z打开apk文件观察发现有如下3个针对加密的文件。

xxx.apk\assets\bin\Data\Managed\Metadata\global-metadata.dat
xxx.apk\lib\armeabi-v7a\libil2cpp.so
xxx.apk\lib\arm64-v8a\libil2cpp.so

xxx.aab\base\assets\bin\Data\Managed\Metadata\global-metadata.dat
xxx.aab\base\lib\armeabi-v7a\libil2cpp.so
xxx.aab\base\lib\arm64-v8a\libil2cpp.so

如上打包配置:Player Settings - Other Settings - Scripting Backend 选IL2CPP
若选Mono,libil2cpp.so会变成libmono.so,针对.so文件的加密较为复杂可参考libmono.so打包
【Unity3D】unity-mono编译libmono.so成功-CSDN博客

本文章仅针对global-metadata.dat文件加密,global-metadata.dat是一种元数据文件,包含了编译后的IL代码所需要的所有信息,包括类型信息、方法信息、字段信息等等。

它由两部分组成:文件头和元数据。
文件头包含了两个重要的信息:魔数和版本号。
魔数是一个4字节的标志,用于确定这个文件是Unity的元数据文件,它的值是0xFAB11BAF。
版本号是一个4字节的无符号整数,用于表示Unity引擎的版本号,它的值为24(0x18)

HxD Hex Editor工具查看

using UnityEngine;
using UnityEditor;
using System.IO;

public static class EncryptEditor
{
    [MenuItem("Tools/加密global-metadata.dat")]
    public static void Encrypt()
    {
        string path = Application.dataPath + "/global-metadata.dat";
        string key_char = "abcd ";
        byte[] bytes = File.ReadAllBytes(path);
        for (int i = 0; i < bytes.Length; i++)
        {
            int keyCharIndex = i % key_char.Length;
            if (keyCharIndex == 4)
            {
                continue;
            }
            bytes[i] = (byte)(bytes[i] ^ key_char[i % key_char.Length]);
        }
        File.WriteAllBytes(path, bytes);
    }
}

加密后 

找到Unity工程安装目录

\Editor\Data\il2cpp\libil2cpp\vm\MetadataLoader.cpp (选择IL2CPP打包方式)
\Editor\Data\il2cpp\libmono\vm\MetadataLoader.cpp (选择Mono打包方式)

#include "il2cpp-config.h"
#include "MetadataLoader.h"
#include "os/File.h"
#include "os/Mutex.h"
#include "utils/MemoryMappedFile.h"
#include "utils/PathUtils.h"
#include "utils/Runtime.h"
#include "utils/Logging.h"

void* il2cpp::vm::MetadataLoader::LoadMetadataFile(const char* fileName)
{
    std::string resourcesDirectory = utils::PathUtils::Combine(utils::Runtime::GetDataDir(), utils::StringView<char>("Metadata"));

    std::string resourceFilePath = utils::PathUtils::Combine(resourcesDirectory, utils::StringView<char>(fileName, strlen(fileName)));

    int error = 0;
    os::FileHandle* handle = os::File::Open(resourceFilePath, kFileModeOpen, kFileAccessRead, kFileShareRead, kFileOptionsNone, &error);
    if (error != 0)
    {
        utils::Logging::Write("ERROR: Could not open %s", resourceFilePath.c_str());
        return NULL;
    }
	//解密相关改动 读取数据长度
	int64_t length = 0;
	int error2 = 0;
	length = os::File::GetLength(handle, &error2);
	//解密相关改动
    void* fileBuffer = utils::MemoryMappedFile::Map(handle);

    os::File::Close(handle, &error);
    if (error != 0)
    {
        utils::MemoryMappedFile::Unmap(fileBuffer);
        fileBuffer = NULL;
        return NULL;
    }
	
	//解密相关改动 拷贝数据至data,解密data
	char *data = (char*)malloc(length);
	memcpy(data, fileBuffer, length);
	void* result = utils::MemoryMappedFile::DecryptFile(data, length);
	return result;
	//解密相关改动

	//注释源代码
    //return fileBuffer;
}

\Editor\Data\il2cpp\libil2cpp\utils\MemoryMappedFile.cpp

	void* MemoryMappedFile::DecryptFile(char* data, int64_t length)
	{
		char *result;
		result = (char*)malloc(length);
		char a[5] = "abcd";
		for (int i = 0; i < length; i++)
		{
			result[i] = data[i] ^ a[i%5];
		}
		return static_cast<void*>(result);
	}

\Editor\Data\il2cpp\libil2cpp\utils\MemoryMappedFile.h

#pragma once

#include <map>
#include "os/File.h"
#include "os/Mutex.h"
#include "os/MemoryMappedFile.h"

namespace il2cpp
{
namespace utils
{
    class MemoryMappedFile
    {
    public:
        static void* Map(os::FileHandle* file);
        static void* Map(os::FileHandle* file, int64_t length, int64_t offset);
        static void* Map(os::FileHandle* file, int64_t length, int64_t offset, int32_t access);
		static void* DecryptFile(char* data, int64_t length);
        static bool Unmap(void* address);
        static bool Unmap(void* address, int64_t length);
    };
}
}

修改完毕后,进行打包工程apk,得到test.apk,然后按如下流程进行反编译,加密,重签名打包。

使用APKTool库反编译test.apk得到test文件夹
apktool d test.apk -o test

进入test文件夹,将\test\assets\bin\Data\Managed\Metadata\global-metadata.dat挪到项目里加密,然后再覆盖test文件夹内的,接着进行重打包apk得到ex_test.apk
apktool b test -o ex_test.apk

使用Android Studio(3.5.1)创建keystore_test.jks,alias别名:key3  密码:123456
jarsigner -verbose -keystore keystore_test.jks -signedjar xxx_signed.apk ex_test.apk key3

使用zipalign对签名后的文件进行对齐
zipalign -p -f -v 4 xxx_signed.apk xxx_zipalign.apk

文件对齐后还需要使用apksigner,进行签名
apksigner sign --ks keystore_test.jks --ks-key-alias key3 --ks-pass pass:123456 --v2-signing-enabled true -v --out final.apk xxx_zipalign.apk

注意事项: 

针对global-metadata.dat文件的加密和解密写法看着有点奇怪,是不是?
因为C++的char[]数组会末尾自动添加'\0'结束符

char a[5] = "abcd";
for (int i = 0; i < length; i++)
{
    result[i] = data[i] ^ a[i%5];
}

上面这个操作,当i%5=4时,取到a[4]是'\0',data[i] ^ '\0' = data[i] 不会发生变化的,因此C#侧加密代码需要如下写法,当i%5=4时,我直接continue跳过,不对bytes[i]做处理。

string key_char = "abcd ";
for (int i = 0; i < bytes.Length; i++)
{
    int keyCharIndex = i % key_char.Length;
    if (keyCharIndex == 4)
    {
        continue;
    }
    bytes[i] = (byte)(bytes[i] ^ key_char[i % key_char.Length]);
}

1、如果apk无法安装,说明没有正常签名以及对齐文件再签名。
2、如果apk可以安装,但运行会闪退说明加密或解密操作出问题,或者真的是项目apk有问题,需确保apk没有解密操作情况下也能正常跑。 (也就是不要修改MetadataLoader.cpp文件 全部还原先)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2274705.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器学习之贝叶斯分类器和混淆矩阵可视化

贝叶斯分类器 目录 贝叶斯分类器1 贝叶斯分类器1.1 概念1.2算法理解1.3 算法导入1.4 函数 2 混淆矩阵可视化2.1 概念2.2 理解2.3 函数导入2.4 函数及参数2.5 绘制函数 3 实际预测3.1 数据及理解3.2 代码测试 1 贝叶斯分类器 1.1 概念 贝叶斯分类器是基于贝叶斯定理构建的分类…

前端报告 2024:全新数据,深度解析未来趋势

温馨提示: 此报告为国际版全球报告,其中所涉及的技术应用、工具偏好、开发者习惯等情况反映的是全球前端开发领域的综合态势。由于国内外技术发展环境、行业生态以及企业需求等存在差异,可能有些内容并不完全契合国内的实际情况,请大家理性阅读,批判性地吸收其中的观点与信…

科普CMOS传感器的工作原理及特点

在当今数字化成像的时代&#xff0c;图像传感器无疑是幕后的关键 “功臣”&#xff0c;它宛如一位神奇的 “光影魔法师”&#xff0c;通过光电效应这一奇妙的物理现象&#xff0c;将光子巧妙地转换成电荷&#xff0c;为图像的诞生奠定基础。而在众多类型的图像传感器中&#xf…

word论文排版常见问题汇总

word论文排版常见问题汇总 常用快捷键&#xff1a; Alt F9 正常模式与域代码模式切换 Ctrl F9 插入域代码 F9 刷新域代码显示&#xff0c;要注意选定后刷新才会有效果 word中在当前列表的基础上修改列表 在使用word时&#xff0c;我们会定义一个列表&#xff0c;并将其链接…

使用PVE快速创建虚拟机集群并搭建docker环境

安装Linux系统 这里以安装龙蜥操作系统AnolisOS8.9为例加以说明。 通过PVE后台上传操作系统ISO镜像。 然后在PVE上【创建虚拟机】&#xff0c;选定上传的龙蜥操作系统镜像进行系统安装。 注意&#xff1a;在安装过程中&#xff0c;要设定语言、时区、超管用户root的密码、普…

某音响制造公司发展战略转型项目成功案例纪实

面对产业结构变化、海外订单缩减、劳动力成本攀升、缺乏自主品牌等原因导致的利润空间急剧下降的挑战&#xff0c;面向海外市场的代工厂如何在严峻的经济形势下克服发展障碍&#xff0c;成功实现转型与发展&#xff1f; 某音响制造公司&#xff0c;面临着订单量减少、成本增高…

redis的学习(三)

6. set集合 集合&#xff1a;把一些有关联的数据放在一起。 1、集合中的元素是无序的&#xff0c;即数据存放顺序不重要&#xff0c;变化一下顺序&#xff0c;集合依旧是之前的集合。 2、集合中的元素是不能重复的&#xff08;唯一性&#xff09;与list类似的是集合中的每一个元…

点赞系统设计(微服务)

点赞业务是一个常见的社交功能&#xff0c;它允许用户对其他用户的内容&#xff08;如帖子、评论、图片等&#xff09;表示喜欢或支持。在设计点赞业务时&#xff0c;需要考虑以下几个方面&#xff1a; 一、业务需求 点赞业务需要满足以下特性&#xff1a; 通用&#xff1a;…

C#进阶-在Ubuntu上部署ASP.NET Core Web API应用

随着云计算和容器化技术的普及&#xff0c;Linux 服务器已成为部署 Web 应用程序的主流平台之一。ASP.NET Core 作为一个跨平台、高性能的框架&#xff0c;非常适合在 Linux 环境中运行。本篇博客将详细介绍如何在 Linux 服务器上部署 ASP.NET Core Web API 应用&#xff0c;包…

设计模式-结构型-桥接模式

1. 什么是桥接模式&#xff1f; 桥接模式&#xff08;Bridge Pattern&#xff09; 是一种结构型设计模式&#xff0c;它旨在将抽象部分与实现部分分离&#xff0c;使它们可以独立变化。通过这种方式&#xff0c;系统可以在抽象和实现两方面进行扩展&#xff0c;而无需相互影响…

python学习笔记—16—数据容器之元组

1. 元组——tuple(元组是一个只读的list) (1) 元组的定义注意&#xff1a;定义单个元素的元组&#xff0c;在元素后面要加上 , (2) 元组也支持嵌套 (3) 下标索引取出元素 (4) 元组的相关操作 1. index——查看元组中某个元素在元组中的位置从左到右第一次出现的位置 t1 (&qu…

基础算法--查找

一、线性枚举 1、线性枚举定义 线性枚举指的就是遍历某个一维数组&#xff08;顺序表&#xff09;的所有元素&#xff0c;找到满足条件的那个元素并且返回&#xff0c;返回值可以是下标&#xff0c;也可以是元素本身。 由于是遍历的&#xff0c;穷举了所有情况&#xff0c;所…

G1垃圾回收器的FullGC

如何确定GarbageFirst回收器发生的是FullGC ? 必须出现FullGC字样才算是FUllGC&#xff0c;例如下图&#xff1a;因为内存分配失败&#xff08;Allocation Failure&#xff09;导致 如果不出现FullGC的字样说明它不是FUllGC&#xff0c;并不像Serial GC、ParallelGC的在老年代…

Golang的代码压缩技术应用案例分析与研究实践

Golang的代码压缩技术应用案例分析与研究实践 一、介绍 是一种具有强大性能和便捷开发特性的编程语言&#xff0c;除了其优秀的语法和标准库外&#xff0c;它还拥有很多高级特性&#xff0c;其中之一就是代码压缩技术。本文将从常见的Golang代码压缩技术应用案例出发&#xff0…

【Uniapp-Vue3】image媒体组件属性

如果我们想要在页面上展示图片就需要使用到image标签。 这部分最重要的是图片的裁剪&#xff0c;图片的裁剪和缩放属性&#xff1a; mode 图片裁剪、缩放的模式 默认值是scaleToFill 我将用两张图片对属性进行演示&#xff0c;一张是pic1.jpg&#xff08;宽更长&#xf…

【网络协议】交换机概念与配置(第一部分)

概述 本文将探讨交换机的概念以及交换机的基础配置&#xff0c;并以此引入对 VLAN 的讨论。 文章目录 概述CSMA/CD以太网通信单播&#xff08;Unicast&#xff09;多播&#xff08;Multicast&#xff09;广播&#xff08;Broadcast&#xff09; MAC 地址以太网中的双工设置半双…

oracle位运算、左移右移、标签算法等

文章目录 位运算基础与或非同或同或应用场景 异或异或应用场景 什么是真值表 oracle基础函数创建bitor(按位或)函数bitnot(按位非)函数bitxor(按位异或)函数左移函数BITSHIFT()函数(实测不可用&#xff0c;废弃掉该方案)右移函数(略&#xff0c;有此场景吗?) 实际应用资质字典…

(五)ROS通信编程——参数服务器

前言 参数服务器在ROS中主要用于实现不同节点之间的数据共享&#xff08;P2P&#xff09;。参数服务器相当于是独立于所有节点的一个公共容器&#xff0c;可以将数据存储在该容器中&#xff0c;被不同的节点调用&#xff0c;当然不同的节点也可以往其中存储数据&#xff0c;关…

《零基础Go语言算法实战》【题目 1-18】切片的反转

《零基础Go语言算法实战》 【题目 1-18】切片的反转 请编写一个名为 reverse 的函数&#xff0c;采用整数切片并在不使用临时切片的情况下将切片反转。 【解答】 可以通过 for 循环交换切片中每个元素的值&#xff0c;使其从左向右滑动。最终&#xff0c;所有元素都将 被反转。…

Elasticsearch:搜索相关性

这里写目录标题 一、相关性的概述二、自定义评分策略1、TF-IDF算法2、BM25算法 三、自定义评分策略1、Index Boost&#xff1a;在索引层面修改相关性2、boosting&#xff1a;修改文档相关性3、negative_boost&#xff1a;降低相关性4、function_score&#xff1a;自定义评分5、…