├── .gitignore ├── Native.cpp ├── image.png ├── README_RES ├── go.gif ├── xor.gif ├── encrypt.jpg ├── import.jpg ├── decrtypt1.jpg ├── decrtypt2.jpg ├── global-metadata-cpp.jpg └── global-metedata-loader.jpg ├── pch.cpp ├── plugins └── EasyObfuscation.unitypackage ├── framework.h ├── Il2cppEncrtypt.vcxproj.user ├── pch.h ├── Native.h ├── dllmain.cpp ├── File.hpp ├── Il2cppEncrtypt.sln ├── Il2cppEncrtypt.vcxproj.filters ├── Il2cppEncrtypt.vcxproj └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/* 2 | x64/* 3 | -------------------------------------------------------------------------------- /Native.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badApple001/Il2cppEncrtypt/HEAD/Native.cpp -------------------------------------------------------------------------------- /image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badApple001/Il2cppEncrtypt/HEAD/image.png -------------------------------------------------------------------------------- /README_RES/go.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badApple001/Il2cppEncrtypt/HEAD/README_RES/go.gif -------------------------------------------------------------------------------- /README_RES/xor.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badApple001/Il2cppEncrtypt/HEAD/README_RES/xor.gif -------------------------------------------------------------------------------- /pch.cpp: -------------------------------------------------------------------------------- 1 | // pch.cpp: 与预编译标头对应的源文件 2 | 3 | #include "pch.h" 4 | 5 | // 当使用预编译的头时,需要使用此源文件,编译才能成功。 6 | -------------------------------------------------------------------------------- /README_RES/encrypt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badApple001/Il2cppEncrtypt/HEAD/README_RES/encrypt.jpg -------------------------------------------------------------------------------- /README_RES/import.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badApple001/Il2cppEncrtypt/HEAD/README_RES/import.jpg -------------------------------------------------------------------------------- /README_RES/decrtypt1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badApple001/Il2cppEncrtypt/HEAD/README_RES/decrtypt1.jpg -------------------------------------------------------------------------------- /README_RES/decrtypt2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badApple001/Il2cppEncrtypt/HEAD/README_RES/decrtypt2.jpg -------------------------------------------------------------------------------- /README_RES/global-metadata-cpp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badApple001/Il2cppEncrtypt/HEAD/README_RES/global-metadata-cpp.jpg -------------------------------------------------------------------------------- /README_RES/global-metedata-loader.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badApple001/Il2cppEncrtypt/HEAD/README_RES/global-metedata-loader.jpg -------------------------------------------------------------------------------- /plugins/EasyObfuscation.unitypackage: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badApple001/Il2cppEncrtypt/HEAD/plugins/EasyObfuscation.unitypackage -------------------------------------------------------------------------------- /framework.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容 4 | #define _CRT_SECURE_NO_WARNINGS 5 | // Windows 头文件 6 | #include -------------------------------------------------------------------------------- /Il2cppEncrtypt.vcxproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /pch.h: -------------------------------------------------------------------------------- 1 | // pch.h: 这是预编译标头文件。 2 | // 下方列出的文件仅编译一次,提高了将来生成的生成性能。 3 | // 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。 4 | // 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。 5 | // 请勿在此处添加要频繁更新的文件,这将使得性能优势无效。 6 | 7 | #ifndef PCH_H 8 | #define PCH_H 9 | 10 | // 添加要在此处预编译的标头 11 | #include "framework.h" 12 | 13 | #endif //PCH_H 14 | -------------------------------------------------------------------------------- /Native.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifdef SAMPLE_EXPORTS 3 | #define DLL_CALL __declspec(dllexport) 4 | #else 5 | #define DLL_CALL __declspec(dllimport) 6 | #endif 7 | 8 | typedef void (*LogCallback)(char* message, int iSize); 9 | 10 | 11 | extern "C" DLL_CALL void OverrideLoader(char* path); 12 | 13 | extern "C" DLL_CALL void EncryptionCode(char* path); 14 | 15 | extern "C" DLL_CALL void SetDisplayLog(LogCallback logCall); 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /dllmain.cpp: -------------------------------------------------------------------------------- 1 | // dllmain.cpp : 定义 DLL 应用程序的入口点。 2 | #include "pch.h" 3 | 4 | BOOL APIENTRY DllMain( HMODULE hModule, 5 | DWORD ul_reason_for_call, 6 | LPVOID lpReserved 7 | ) 8 | { 9 | switch (ul_reason_for_call) 10 | { 11 | case DLL_PROCESS_ATTACH: 12 | case DLL_THREAD_ATTACH: 13 | case DLL_THREAD_DETACH: 14 | case DLL_PROCESS_DETACH: 15 | break; 16 | } 17 | return TRUE; 18 | } 19 | 20 | -------------------------------------------------------------------------------- /File.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace File { 5 | 6 | bool Contain(const char* path, const char* dest) { 7 | std::ifstream infile(path, std::ios::in | std::ios::binary | std::ios::ate); 8 | size_t size = infile.tellg(); 9 | infile.seekg(0, std::ios::beg); 10 | char* buffer = new char[size]; 11 | infile.read(buffer, size); 12 | infile.close(); 13 | std::string alltext(buffer, size); 14 | delete[] buffer; 15 | return alltext.find(dest) != std::string::npos; 16 | } 17 | 18 | char* ReadBytes(const char* path) { 19 | std::ifstream infile(path, std::ios::in | std::ios::binary | std::ios::ate); 20 | size_t size = infile.tellg(); 21 | infile.seekg(0, std::ios::beg); 22 | char* buffer = new char[size]; 23 | infile.read(buffer, size); 24 | infile.close(); 25 | return buffer; 26 | } 27 | 28 | std::string Extract(const char* path, size_t offset, size_t length) { 29 | char* buffer = ReadBytes(path); 30 | if (buffer) { 31 | std::string a(buffer + offset, length); 32 | delete[] buffer; 33 | return a; 34 | } 35 | delete[] buffer; 36 | return nullptr; 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /Il2cppEncrtypt.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.8.34316.72 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Il2cppEncrtypt", "Il2cppEncrtypt.vcxproj", "{DF210032-7EF0-47D3-8EAC-2212A73AE20E}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {DF210032-7EF0-47D3-8EAC-2212A73AE20E}.Debug|x64.ActiveCfg = Debug|x64 17 | {DF210032-7EF0-47D3-8EAC-2212A73AE20E}.Debug|x64.Build.0 = Debug|x64 18 | {DF210032-7EF0-47D3-8EAC-2212A73AE20E}.Debug|x86.ActiveCfg = Debug|Win32 19 | {DF210032-7EF0-47D3-8EAC-2212A73AE20E}.Debug|x86.Build.0 = Debug|Win32 20 | {DF210032-7EF0-47D3-8EAC-2212A73AE20E}.Release|x64.ActiveCfg = Release|x64 21 | {DF210032-7EF0-47D3-8EAC-2212A73AE20E}.Release|x64.Build.0 = Release|x64 22 | {DF210032-7EF0-47D3-8EAC-2212A73AE20E}.Release|x86.ActiveCfg = Release|Win32 23 | {DF210032-7EF0-47D3-8EAC-2212A73AE20E}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {2A077165-5DA3-4BB2-8F8F-1C7D116D1DEF} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /Il2cppEncrtypt.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | 头文件 20 | 21 | 22 | 头文件 23 | 24 | 25 | 头文件 26 | 27 | 28 | 头文件 29 | 30 | 31 | 32 | 33 | 源文件 34 | 35 | 36 | 源文件 37 | 38 | 39 | 源文件 40 | 41 | 42 | -------------------------------------------------------------------------------- /Il2cppEncrtypt.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 17.0 23 | Win32Proj 24 | {df210032-7ef0-47d3-8eac-2212a73ae20e} 25 | Il2cppEncrtypt 26 | 10.0 27 | 28 | 29 | 30 | DynamicLibrary 31 | true 32 | v143 33 | Unicode 34 | 35 | 36 | DynamicLibrary 37 | false 38 | v143 39 | true 40 | Unicode 41 | 42 | 43 | DynamicLibrary 44 | true 45 | v143 46 | Unicode 47 | 48 | 49 | DynamicLibrary 50 | false 51 | v143 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | EasyObfuscation 75 | 76 | 77 | EasyObfuscation 78 | 79 | 80 | 81 | Level3 82 | true 83 | WIN32;_DEBUG;IL2CPPENCRTYPT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 84 | true 85 | Use 86 | pch.h 87 | 88 | 89 | Windows 90 | true 91 | false 92 | 93 | 94 | 95 | 96 | Level3 97 | true 98 | true 99 | true 100 | WIN32;NDEBUG;IL2CPPENCRTYPT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 101 | true 102 | Use 103 | pch.h 104 | 105 | 106 | Windows 107 | true 108 | true 109 | true 110 | false 111 | 112 | 113 | 114 | 115 | Level3 116 | true 117 | _DEBUG;IL2CPPENCRTYPT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 118 | true 119 | Use 120 | pch.h 121 | 122 | 123 | Windows 124 | true 125 | false 126 | 127 | 128 | 129 | 130 | Level3 131 | true 132 | true 133 | true 134 | NDEBUG;IL2CPPENCRTYPT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 135 | true 136 | Use 137 | pch.h 138 | 139 | 140 | Windows 141 | true 142 | true 143 | true 144 | false 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | Create 158 | Create 159 | Create 160 | Create 161 | 162 | 163 | 164 | 165 | 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unity Global-metadata 源码映射文件加密 2 | - 可以实现 Android APK加固 3 | - 可以实现 iOS 混淆马甲包处理 ( 暂不公开 ) 4 | - 可以实现 代码混淆 5 | 6 | ## Unitypackage download 7 | 8 | [./plugins/EasyObfuscation.unitypackage](./plugins/EasyObfuscation.unitypackage) 9 | 10 | ### 使用方法 11 | 12 | 导入Unitypackage到项目中 输出Android工程打包 即可 13 | ![](./README_RES/import.jpg) 14 | 15 | ### 验证加密是否成功 16 | 17 | > 使用Il2CppDumper 检查是否能被破解 18 | 19 | [bilibili 视频教程](https://www.bilibili.com/video/BV1CQ4y137Gs/?vd_source=144e0a15a312c88bfe222df707be48d5) 20 | 21 | ## global-metadata.dat是什么? 22 | 23 | 在Unity引擎中,global-metadata.dat是一种元数据文件,包含了编译后的IL代码所需要的所有信息,包括类型信息、方法信息、字段信息等等。它是一种二进制文件,通常保存在Unity工程的Data文件夹下的il2cpp目录中。 24 | 25 | ### global-metadata.dat的作用 26 | 27 | global-metadata.dat文件的作用是为了在运行时将IL代码转换为C++代码。IL2CPP(Intermediate Language to C++)是一种Unity引擎的构建选项,它可以将C#代码编译为IL代码,然后再将IL代码转换为C++代码,从而提高游戏的性能和安全性。 28 | 29 | 在转换过程中,global-metadata.dat文件是非常重要的,它包含了编译后的IL代码所需要的所有信息。IL2CPP将global-metadata.dat文件中的元数据信息与转换后的C++代码进行关联,使得C++代码可以在运行时正确地调用和访问IL代码。 30 | 31 | ### global-metadata.dat的底层原理 32 | 33 | 在Unity引擎中,global-metadata.dat文件的生成是通过IL2CPP工具链完成的。IL2CPP将编译后的IL代码解析成一个个结构体,这些结构体包含了IL代码的类型信息、方法信息、字段信息等等。然后,IL2CPP将这些结构体存储到一个二进制文件中,即global-metadata.dat文件。 34 | 35 | 在运行时,IL2CPP会将global-metadata.dat文件加载到内存中,并将元数据信息与转换后的C++代码进行关联。当C++代码需要访问IL代码的类型信息、方法信息、字段信息等等时,IL2CPP会根据global-metadata.dat文件中的元数据信息,从内存中获取相应的信息,并将其转换为C++代码。 36 | 37 | ### 反编译和修改 38 | 39 | Global-metadata.dat文件也可以被反编译,以获取游戏项目的元数据信息。这对于游戏开发者和研究人员来说是非常有用的,因为他们可以在不需要访问代码的情况下了解游戏的结构和实现细节。不过需要注意的是,修改Global-metadata.dat文件可能会导致游戏的运行出现问题或不正常,因此除非有必要,一般不建议修改。 40 | 41 | 下方我们看下Global-metadata.dat在Unity当中的调用。 42 | ![](./README_RES/global-metadata-cpp.jpg) 43 | 我们看到MetadataCache的Initialize()接口调用了这个文件,而这个接口的调用时机就是游戏启动的时候,然后Initialize接口就会解析这个文件的信息,然后缓存起来供后续使用。 44 | 45 | ### Global-metadata.dat文件的安全性 46 | 47 | 由于Global-metadata.dat文件包含了游戏项目的元数据信息,因此它也包含了一些敏感信息,如类名、方法名等。这些信息可以被黑客用来进行反编译和破解游戏,因此需要注意保护文件的安全性。 48 | 49 | 一般来说,Unity引擎会将Global-metadata.dat文件加密,以防止它被直接访问和修改。只有在运行时,引擎才会将文件解密并加载到内存中。这种加密方式可以一定程度上保护文件的安全性,但并不能完全防止黑客的攻击。因此,游戏开发者还需要在代码层面上进行安全措施,如加密敏感信息、限制代码访问等 50 | 51 | ## 加密方案 52 | 53 | ### unity打包后 global-metadata.dat 去哪了? 54 | 55 | 输出Android工程路径\\unityLibrary\\src\\main\\assets\\bin\\Data\\Managed\\Metadata\\global-metadata.dat 56 | 57 | ### 如何监听unity打包事件? 输出Android工程路径怎么获取 ? 58 | 59 | 扩展IPostprocessBuildWithReport接口 60 | 61 | 案例 62 | 63 | ```csharp 64 | public class MyCustomBuildProcessor : IPostprocessBuildWithReport 65 | { 66 | int IOrderedCallback.callbackOrder { get { return 0; } } 67 | 68 | void IPostprocessBuildWithReport.OnPostprocessBuild( BuildReport report ) 69 | { 70 | 71 | //输出打包后的Android工程路径 72 | Debug.Log( report.summary.outputPath ); 73 | } 74 | } 75 | ``` 76 | 77 | ### 拿到global-metadata.dat后怎么加密 78 | 79 | 使用异或特性对二进制文件进行加密 80 | ![](./README_RES/xor.gif) 81 | 82 | ```cpp 83 | std::string Encrypt(std::string content, std::string secretKey) 84 | { 85 | for (UINT i = 0; i < content.length(); i++) 86 | { 87 | content[i] ^= secretKey[i % secretKey.length()]; 88 | } 89 | 90 | return content; 91 | } 92 | 93 | std::string Decrypt(std::string data, std::string secretKey) 94 | { 95 | for (UINT i = 0; i < data.length(); i++) 96 | { 97 | data[i] ^= secretKey[i % secretKey.length()]; 98 | } 99 | 100 | return data; 101 | } 102 | ``` 103 | 104 | ### 加密案例 105 | 106 | > 调用EncryptionCode函数并传入Android工程路径 对global-metadata文件进行加密混淆 107 | 108 | **案例加密方式图解** 109 | ![](./README_RES/encrypt.jpg) 110 | 111 | ```cpp 112 | 113 | char* encrtypt_file(char* src, size_t& file_size) { 114 | 115 | //随机密钥长度 116 | uniform_int_distribution key_distrib(130, 140); 117 | int kl = key_distrib(s_randomEngine); 118 | 119 | //随机密钥的数组指针 120 | unsigned int* p_passwordArr = new unsigned int[kl]; 121 | for (int i = 0; i < kl; i++) 122 | { 123 | p_passwordArr[i] = get_random_uint(); 124 | } 125 | 126 | //加密区长度 单位:int指针 127 | int klsize = (kl + 1) * sizeof(uint32_t); 128 | const int safe_size = 1024;//安全区大小 129 | //加密区大小 130 | const size_t encrtypt_size = file_size - safe_size; 131 | //申请一个新的内存卡 它将包含密码和源文件 132 | char* des = (char*)malloc(file_size + klsize); 133 | //将安全区代码Cpy到新的内存块 134 | memcpy(des, src, safe_size); 135 | 136 | //密文区指针 137 | unsigned int* da = (unsigned int*)(des + safe_size); 138 | //加密源码区指针 139 | unsigned int* db = (unsigned int*)(src + safe_size); 140 | //密文区首四个字节低十六位为加密密文数组长度 141 | *(da++) = (get_random_uint() & 0xFFFF0000) | (kl & 0xFFFF); 142 | //写入密码组 143 | memcpy(da, p_passwordArr, kl * sizeof(uint32_t)); 144 | //指向加密区 145 | da += kl; 146 | 147 | for (size_t i = 0; i < encrtypt_size; i += 4) { 148 | int index = (i + (i / kl)) % kl; 149 | da[i / 4] = p_passwordArr[index] ^ db[i / 4]; 150 | } 151 | 152 | file_size += klsize; 153 | delete[] p_passwordArr; 154 | return des; 155 | } 156 | 157 | void EncryptionCode(char* export_android_path) 158 | { 159 | //wait input 160 | string global_metadata_path = export_android_path; 161 | if (NULL == strstr(global_metadata_path.c_str(), "global-metadata.dat")) { 162 | global_metadata_path += "\\unityLibrary\\src\\main\\assets\\bin\\Data\\Managed\\Metadata\\global-metadata.dat"; 163 | } 164 | 165 | //cheack file vaild 166 | if (!file_exist(global_metadata_path.c_str())) { 167 | Log("file not found: %s\n", global_metadata_path); 168 | return; 169 | } 170 | 171 | Log("EasyObfuscation version: %s", version); 172 | 173 | //load file 174 | ifstream infile(global_metadata_path, ios::in | ios::binary | ios::ate); 175 | size_t size = infile.tellg(); 176 | infile.seekg(0, ios::beg); 177 | char* buffer = new char[size]; 178 | infile.read(buffer, size); 179 | infile.close(); 180 | 181 | //encrtypt 182 | size_t srcsize = size; 183 | char* encbuffer = encrtypt_file(buffer, size); 184 | ofstream outfile(global_metadata_path, ios::out | ios::binary | ios::ate); 185 | if (!outfile) { 186 | Log("open file fail: %s\n", global_metadata_path); 187 | return; 188 | } 189 | 190 | //log 191 | unsigned int* hex_buffer = (unsigned int*)buffer; 192 | unsigned int* hex_encbuffer = (unsigned int*)encbuffer; 193 | unsigned int src_value = get_little_endian(*hex_buffer); 194 | unsigned int enc_value = get_little_endian(*hex_encbuffer); 195 | Log("src: %x\tsrc buffer size: %ld\nenc: %x\tenc buffer size: %ld", src_value, srcsize, enc_value, size); 196 | 197 | outfile.write(encbuffer, size); 198 | outfile.close(); 199 | delete[] buffer; 200 | free(encbuffer); 201 | 202 | Log("call cpp complete."); 203 | } 204 | ``` 205 | 206 | ### 修改unity引擎il2cpp源码 加载并还原真实文件内存 207 | 208 | > 我们需要修改 MetadataLoader.cpp 文件 209 | > 对fileBuffer文件进行加工处理 210 | > MetadataLoader.cpp路径 C:\Program Files\Unity\Hub\Editor\2021.3.22f1\Editor\Data\il2cpp\libil2cpp\vm 211 | > unity的安装路径下\Editor\Data\il2cpp\libil2cpp\vm 212 | 213 | ![](./README_RES/global-metedata-loader.jpg) 214 | ------- 215 | 216 | #### 第一步 先实现解密方法 217 | 218 | ![](./README_RES/decrtypt1.jpg) 219 | 220 | ```cpp 221 | void *PromiseAntiencryption(void *src, int64_t len) 222 | { 223 | 224 | char *cp_src = (char *)src; // 以字节流形式访问文件内存buffer 225 | const int safe_size = 1024; // 安全区大小 226 | // 机密区首四个字节低十六位为加密密文数组长度 227 | unsigned int *ip_mask = (unsigned int *)(cp_src + safe_size); 228 | // 获取加密区掩码数组长度 229 | int kl = (int)((*ip_mask) & 0xffff); 230 | // 获取加密代码长度 231 | const int64_t code_segment_size = len - sizeof(uint32_t) * (kl + 1); 232 | const int64_t code_encryption_size = code_segment_size - safe_size; 233 | // 计算真实代码的内存映射长度并申请一块新的内存块 234 | char *buffer = (char *)malloc(code_segment_size); 235 | // 将安全区代码Copy到新的内存块中 236 | memcpy(buffer, cp_src, safe_size); 237 | // 获取代码区地址指针 238 | unsigned int *ip_data = (unsigned int *)(buffer + safe_size); 239 | unsigned int *t = ip_mask + 1;//密文数组指针 240 | unsigned int *d = ip_mask + kl + 1;//加密区地址指针 241 | // 反加密处理 242 | for (int64_t i = 0; i < code_encryption_size; i += 4) 243 | { 244 | *(ip_data + i / 4) = (*(t + ((i + (i / kl)) % kl))) ^ (*(d + i / 4)); 245 | } 246 | return (void *)buffer; 247 | } 248 | ``` 249 | 250 | #### 第二步 调用 251 | 252 | ![](./README_RES/decrtypt2.jpg) 253 | 254 | ### 完整MetadataLoader源码 255 | 256 | ```cpp 257 | #include "il2cpp-config.h" 258 | #include "MetadataLoader.h" 259 | #include "os/File.h" 260 | #include "os/Mutex.h" 261 | #include "utils/MemoryMappedFile.h" 262 | #include "utils/PathUtils.h" 263 | #include "utils/Runtime.h" 264 | #include "utils/Logging.h" 265 | 266 | #if IL2CPP_TARGET_ANDROID && IL2CPP_TINY_DEBUGGER && !IL2CPP_TINY_FROM_IL2CPP_BUILDER 267 | #include 268 | extern "C" 269 | { 270 | void *loadAsset(const char *path, int *size, void *(*alloc)(size_t)); 271 | } 272 | #elif IL2CPP_TARGET_JAVASCRIPT && IL2CPP_TINY_DEBUGGER && !IL2CPP_TINY_FROM_IL2CPP_BUILDER 273 | extern void *g_MetadataForWebTinyDebugger; 274 | #endif 275 | 276 | void *PromiseAntiencryption(void *src, int64_t len) 277 | { 278 | 279 | char *cp_src = (char *)src; // 以字节流形式访问文件内存buffer 280 | const int safe_size = 1024; // 安全区大小 281 | // 机密区首四个字节低十六位为加密密文数组长度 282 | unsigned int *ip_mask = (unsigned int *)(cp_src + safe_size); 283 | // 获取加密区掩码数组长度 284 | int kl = (int)((*ip_mask) & 0xffff); 285 | // 获取加密代码长度 286 | const int64_t code_segment_size = len - sizeof(uint32_t) * (kl + 1); 287 | const int64_t code_encryption_size = code_segment_size - safe_size; 288 | // 计算真实代码的内存映射长度并申请一块新的内存块 289 | char *buffer = (char *)malloc(code_segment_size); 290 | // 将安全区代码Copy到新的内存块中 291 | memcpy(buffer, cp_src, safe_size); 292 | // 获取代码区地址指针 293 | unsigned int *ip_data = (unsigned int *)(buffer + safe_size); 294 | unsigned int *t = ip_mask + 1;//密文数组指针 295 | unsigned int *d = ip_mask + kl + 1;//加密区地址指针 296 | // 反加密处理 297 | for (int64_t i = 0; i < code_encryption_size; i += 4) 298 | { 299 | *(ip_data + i / 4) = (*(t + ((i + (i / kl)) % kl))) ^ (*(d + i / 4)); 300 | } 301 | return (void *)buffer; 302 | } 303 | 304 | extern void *g_cacheFileHeader = NULL; 305 | extern void *g_cacheDecodeHeader = NULL; 306 | void *il2cpp::vm::MetadataLoader::LoadMetadataFile(const char *fileName) 307 | { 308 | #if IL2CPP_TARGET_ANDROID && IL2CPP_TINY_DEBUGGER && !IL2CPP_TINY_FROM_IL2CPP_BUILDER 309 | std::string resourcesDirectory = utils::PathUtils::Combine(utils::StringView("Data"), utils::StringView("Metadata")); 310 | 311 | std::string resourceFilePath = utils::PathUtils::Combine(resourcesDirectory, utils::StringView(fileName, strlen(fileName))); 312 | 313 | int size = 0; 314 | return loadAsset(resourceFilePath.c_str(), &size, malloc); 315 | #elif IL2CPP_TARGET_JAVASCRIPT && IL2CPP_TINY_DEBUGGER && !IL2CPP_TINY_FROM_IL2CPP_BUILDER 316 | return g_MetadataForWebTinyDebugger; 317 | #else 318 | std::string resourcesDirectory = utils::PathUtils::Combine(utils::Runtime::GetDataDir(), utils::StringView("Metadata")); 319 | 320 | std::string resourceFilePath = utils::PathUtils::Combine(resourcesDirectory, utils::StringView(fileName, strlen(fileName))); 321 | 322 | int error = 0; 323 | os::FileHandle *handle = os::File::Open(resourceFilePath, kFileModeOpen, kFileAccessRead, kFileShareRead, kFileOptionsNone, &error); 324 | if (error != 0) 325 | { 326 | utils::Logging::Write("ERROR: Could not open %s", resourceFilePath.c_str()); 327 | return NULL; 328 | } 329 | 330 | void *fileBuffer = g_cacheFileHeader = utils::MemoryMappedFile::Map(handle); 331 | 332 | int ero; 333 | int64_t length = os::File::GetLength(handle, &ero); 334 | void *decBuffer = g_cacheDecodeHeader = PromiseAntiencryption(fileBuffer, length); 335 | 336 | os::File::Close(handle, &error); 337 | if (error != 0) 338 | { 339 | utils::MemoryMappedFile::Unmap(fileBuffer); 340 | fileBuffer = NULL; 341 | return NULL; 342 | } 343 | 344 | return decBuffer; 345 | #endif 346 | } 347 | 348 | void il2cpp::vm::MetadataLoader::UnloadMetadataFile(void *fileBuffer) 349 | { 350 | 351 | if (g_cacheDecodeHeader == fileBuffer) 352 | { 353 | free(fileBuffer); 354 | fileBuffer = g_cacheFileHeader; 355 | } 356 | 357 | #if IL2CPP_TARGET_ANDROID && IL2CPP_TINY_DEBUGGER && !IL2CPP_DEBUGGER_TESTS 358 | free(fileBuffer); 359 | #else 360 | bool success = il2cpp::utils::MemoryMappedFile::Unmap(fileBuffer); 361 | NO_UNUSED_WARNING(success); 362 | IL2CPP_ASSERT(success); 363 | #endif 364 | } 365 | 366 | ``` 367 | 368 | ### 小知识 MemoryMappedFile 是个啥? 369 | 370 | > 内存映射文件 即: 一块内存和一个文件相映射对应 371 | > 内存映射文件与虚拟内存有些类似,通过内存映射文件可以保留一个地址空间的区域,同时将物理存储器提交给此区域,只是内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,而非系统的页文件,而且在对该文件进行操作之前必须首先对文件进行映射,就如同将整个文件从磁盘加载到内存 372 | 373 | ### 进阶技巧 374 | 如果我们直接修改引擎源码 那么没有加密的项目也会被解密 这样的话正常的项目就会运行崩溃 解密失败 375 | 376 | #### 怎么处理呢? 377 | 经过长时间的测试和摸索 我发现 Android输出工程目录下\\unityLibrary\\src\\main\\Il2CppOutputProject\\ 里面有一份Il2Cpp的源码,每次打包时 unity会Copy一份引擎源码到这个工程目录下 所以我们只需要将一份修改后的MetedataLoader脚本在打包后进行替换即可 378 | 379 | #### 感谢观看! 至此 Go! 行动起来吧 380 | ![](./README_RES/go.gif) 381 | 382 | --------------------------------------------------------------------------------