Upload src

This commit is contained in:
Li 2023-05-09 02:17:44 +12:00
parent df72a81caf
commit 11bc128354
43 changed files with 76263 additions and 9 deletions

73
McCrypt/Crypto.cs Normal file
View file

@ -0,0 +1,73 @@
using System;
using System.IO;
using System.Security.Cryptography;
namespace McCrypt
{
internal class Crypto
{
internal static byte[] Sha256(byte[] data)
{
SHA256 sha256 = SHA256.Create();
byte[] hash = sha256.ComputeHash(data);
sha256.Dispose();
return hash;
}
internal static byte[] Aes256CfbEncrypt(byte[] key, byte[] iv, byte[] data)
{
Aes aes = Aes.Create();
aes.Mode = CipherMode.CFB;
aes.Padding = PaddingMode.None;
aes.BlockSize = 128;
aes.KeySize = 256;
ICryptoTransform aesEncryptor = aes.CreateEncryptor(key, iv);
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, aesEncryptor, CryptoStreamMode.Write))
{
csEncrypt.Write(data, 0, data.Length);
long totalWritten = data.Length;
while ((totalWritten % 16 != 0))
{
csEncrypt.WriteByte(0);
totalWritten++;
}
msEncrypt.Seek(0x00, SeekOrigin.Begin);
return msEncrypt.ToArray();
}
}
}
internal static byte[] Aes256CfbDecrypt(byte[] key, byte[] iv, byte[] data)
{
Aes aes = Aes.Create();
aes.Mode = CipherMode.CFB;
aes.Padding = PaddingMode.Zeros;
aes.BlockSize = 128;
aes.KeySize = 256;
ICryptoTransform aesDecryptor = aes.CreateDecryptor(key, iv);
using (MemoryStream msDecrypt = new MemoryStream())
{
msDecrypt.Write(data, 0, data.Length);
while (msDecrypt.Length % 16 != 0)
msDecrypt.WriteByte(0);
msDecrypt.Seek(0x00, SeekOrigin.Begin);
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, aesDecryptor, CryptoStreamMode.Read))
{
byte[] plaintext = new byte[msDecrypt.Length];
csDecrypt.Read(plaintext, 0x00, plaintext.Length);
Array.Copy(plaintext, data, data.Length);
return data;
}
}
}
}
}

302
McCrypt/Keys.cs Normal file
View file

@ -0,0 +1,302 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace McCrypt
{
public class Keys
{
private static Random rng = new Random();
public static string KeyDbFile = "";
internal struct content
{
public string FriendlyId;
public byte[] ContentKey;
}
public struct keysJsonStruct
{
public string id;
public string contentKey;
}
public static string lastTitleAccountId = "";
public static string lastMinecraftId = "";
private static string lastDeviceId = "";
private static List<content> contentList = new List<content>();
public static string ExportKeysJson()
{
List<keysJsonStruct> keysJson = new List<keysJsonStruct>();
foreach (content key in contentList.ToArray())
{
string ckey = Encoding.UTF8.GetString(key.ContentKey);
if (ckey == "s5s5ejuDru4uchuF2drUFuthaspAbepE")
continue;
keysJsonStruct kjs = new keysJsonStruct();
kjs.id = key.FriendlyId;
kjs.contentKey = ckey;
keysJson.Add(kjs);
}
return JsonConvert.SerializeObject(keysJson);
}
private static byte[] deriveUserKey(string UserId, string DeviceId)
{
byte[] userBytes = Encoding.Unicode.GetBytes(UserId);
byte[] deviceBytes = Encoding.Unicode.GetBytes(DeviceId);
int kLen = userBytes.Length;
if (deviceBytes.Length < kLen)
kLen = deviceBytes.Length;
byte[] key = new byte[kLen];
for (int i = 0; i < kLen; i++)
{
key[i] = (byte)(deviceBytes[i] ^ userBytes[i]);
}
return key;
}
internal static string GenerateKey()
{
string allowedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
string key = "";
for (int i = 0; i < 32; i++)
{
key += allowedChars[rng.Next(0, allowedChars.Length)];
}
return key;
}
private static byte[] deriveEntKey(byte[] versionkey, byte[] titleaccountId)
{
int kLen = versionkey.Length;
if (titleaccountId.Length < kLen)
kLen = titleaccountId.Length;
byte[] key = new byte[versionkey.Length];
for (int i = 0; i < kLen; i++)
{
key[i] = (byte)(versionkey[i] ^ titleaccountId[i]);
}
return key;
}
private static byte[] deriveContentKey(byte[] UserKey, byte[] ContentKey)
{
int kLen = UserKey.Length;
if (ContentKey.Length < kLen)
kLen = ContentKey.Length;
byte[] key = new byte[kLen];
for (int i = 0; i < kLen; i++)
{
key[i] = (byte)(UserKey[i] ^ ContentKey[i]);
}
int ckLen = kLen / 2;
byte[] contentKey = new byte[ckLen];
for (int i = 0; i < kLen; i += 2)
{
contentKey[i / 2] = key[i];
}
return contentKey;
}
public static void AddKey(string FriendlyId, byte[] ContentKey, bool addToKeyCache = true)
{
if (LookupKey(FriendlyId) != null)
return;
string keyCacheEntry = FriendlyId + "=" + Encoding.UTF8.GetString(ContentKey);
if (addToKeyCache && KeyDbFile != "")
File.AppendAllText(KeyDbFile, keyCacheEntry + "\n");
content content = new content();
content.FriendlyId = FriendlyId;
content.ContentKey = ContentKey;
contentList.Add(content);
}
private static void readReceipt(string receiptData)
{
dynamic recData = Utils.JsonDecodeCloserToMinecraft(receiptData);
string userId = recData.Receipt.EntityId;
string deviceId = "";
if (recData.Receipt.ReceiptData != null)
deviceId = recData.Receipt.ReceiptData.DeviceId;
if (deviceId == "" || deviceId == null)
deviceId = lastDeviceId;
if (deviceId == "" || deviceId == null)
return;
lastDeviceId = deviceId;
byte[] userKey = deriveUserKey(userId, deviceId);
// Derive content keys
int totalEntitlements = recData.Receipt.Entitlements.Count;
for (int i = 0; i < totalEntitlements; i++)
{
try
{
string friendlyId = recData.Receipt.Entitlements[i].FriendlyId;
string contentKeyB64 = recData.Receipt.Entitlements[i].ContentKey;
if (contentKeyB64 == null)
continue;
byte[] contentKey = Utils.ForceDecodeBase64(contentKeyB64);
byte[] realContentKey = deriveContentKey(userKey, contentKey);
AddKey(friendlyId, realContentKey);
}
catch (Exception) { continue; }
}
}
public static void ReadOptionsTxt(string optionsTxtPath)
{
string[] optionsTxt = File.ReadAllLines(optionsTxtPath);
foreach (string option in optionsTxt)
{
string opt = option.Replace("\r", "").Replace("\n", "").Trim();
string[] kvpair = opt.Split(':');
if (kvpair.Length >= 2)
{
if (kvpair[0].Trim() == "last_minecraft_id")
{
lastMinecraftId = kvpair[1].Trim().ToUpper();
}
if (kvpair[0].Trim() == "last_title_account_id")
{
lastTitleAccountId = kvpair[1].Trim().ToUpper();
}
}
}
}
private static string padLastTileAccountId(string lastTitleAccountId)
{
StringBuilder deriveText = new StringBuilder();
for (int i = 0; i < 32; i++)
deriveText.Append(lastTitleAccountId[i % lastTitleAccountId.Length]);
return deriveText.ToString();
}
private static string decryptEntitlementFile(string encryptedEnt)
{
int version = Int32.Parse(encryptedEnt.Substring(7, 1));
byte[] versionkey;
switch (version)
{
case 2:
default:
versionkey = Encoding.UTF8.GetBytes("X(nG*ejm&E8)m+8c;-SkLTjF)*QdN6_Y");
break;
}
string deriveText = padLastTileAccountId(lastTitleAccountId);
byte[] entKey = deriveEntKey(versionkey, Encoding.UTF8.GetBytes(deriveText));
string entBase64 = encryptedEnt.Substring(8);
byte[] entCiphertext = Utils.ForceDecodeBase64(entBase64);
byte[] entPlaintext = Marketplace.decryptEntitlementBuffer(entCiphertext, entKey);
return Encoding.UTF8.GetString(entPlaintext);
}
public static void ReadEntitlementFile(string entPath)
{
string jsonData = File.ReadAllText(entPath);
if(jsonData.StartsWith("Version")) // Thanks mojang, this was a fun challange <3
{
jsonData = decryptEntitlementFile(jsonData);
}
dynamic entData;
try
{
entData = Utils.JsonDecodeCloserToMinecraft(jsonData);
}
catch (Exception) { return; }
string receiptB64 = entData.Receipt;
if (receiptB64 == null)
return;
if (receiptB64.Split('.').Length <= 1)
return;
string receiptData = Encoding.UTF8.GetString(Utils.ForceDecodeBase64(receiptB64.Split('.')[1]));
readReceipt(receiptData);
int totalItems = entData.Items.Count;
for (int i = 0; i < totalItems; i++)
{
string b64Data = entData.Items[i].Receipt;
if (b64Data == null)
continue;
if (b64Data.Split('.').Length <= 1)
continue;
string recept = Encoding.UTF8.GetString(Utils.ForceDecodeBase64(b64Data.Split('.')[1]));
readReceipt(recept);
}
}
public static void ReadKeysDb(string keyFile)
{
KeyDbFile = keyFile;
string[] keyList = File.ReadAllLines(keyFile);
foreach (string key in keyList)
{
if (key.Contains('='))
{
string[] keys = key.Split('=');
if (keys.Length >= 2)
{
string friendlyId = keys[0];
byte[] contentKey = Encoding.UTF8.GetBytes(keys[1]);
AddKey(friendlyId, contentKey, false);
}
}
}
}
public static byte[] LookupKey(string FriendlyId)
{
foreach (content content in contentList)
{
if (content.FriendlyId == FriendlyId)
return content.ContentKey;
}
return null;
}
}
}

59
McCrypt/LibMcCrypt.csproj Normal file
View file

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{4BEF6F52-6545-4BB9-8053-50335A1C6789}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>McCrypt</RootNamespace>
<AssemblyName>LibMcCrypt</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.IO.Compression.FileSystem" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Crypto.cs" />
<Compile Include="Keys.cs" />
<Compile Include="Manifest.cs" />
<Compile Include="Marketplace.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Utils.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

125
McCrypt/Manifest.cs Normal file
View file

@ -0,0 +1,125 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using System.IO;
using System.Text;
namespace McCrypt
{
public class Manifest
{
private struct signatureBlock
{
public string hash;
public string path;
}
public static string SignManifestString(string manifestJson, string setPath)
{
signatureBlock signBlock = new signatureBlock();
signBlock.path = setPath;
signBlock.hash = Convert.ToBase64String(Crypto.Sha256(Encoding.UTF8.GetBytes(manifestJson)));
List<signatureBlock> signatureData = new List<signatureBlock>();
signatureData.Add(signBlock);
string signatureJson = JsonConvert.SerializeObject(signatureData);
return signatureJson;
}
public static void SignManifest(string basePath)
{
string manifestPath = Path.Combine(basePath, "manifest.json");
signatureBlock signBlock = new signatureBlock();
signBlock.path = manifestPath.Remove(0, basePath.Length + 1);
signBlock.hash = Convert.ToBase64String(Crypto.Sha256(File.ReadAllBytes(manifestPath)));
List<signatureBlock> signatureData = new List<signatureBlock>();
signatureData.Add(signBlock);
string signatureJson = JsonConvert.SerializeObject(signatureData);
string signaturesJsonFile = Path.Combine(basePath, "signatures.json");
File.WriteAllText(signaturesJsonFile, signatureJson);
}
public static string ReadName(string manifestFile)
{
string defaultName = Path.GetFileName(Path.GetDirectoryName(manifestFile));
if (!File.Exists(manifestFile))
return Utils.TrimName(defaultName);
string manifestStr = File.ReadAllText(manifestFile);
dynamic manifestData = JsonConvert.DeserializeObject(manifestStr);
if (manifestData.header != null)
{
if (manifestData.header.name != null)
{
string name = manifestData.header.name;
string englishLanguageFile = Path.Combine(Path.GetDirectoryName(manifestFile), "texts", "en_US.lang");
if (File.Exists(englishLanguageFile))
{
string[] lines = File.ReadAllLines(englishLanguageFile);
foreach (string line in lines)
{
if (!line.Contains('='))
continue;
string[] values = line.Split('=');
// How tf does this work??!!
if (values.Length <= 0)
continue;
if (values[0] == name)
return Utils.TrimName(values[1]);
if (values[0] == "pack.name")
return Utils.TrimName(values[1]);
if (values[0].Contains('.'))
{
string[] values2 = values[0].Split('.');
if (values2.Length <= 0)
return Utils.TrimName(defaultName);
if (values[0].Split('.').Last() == name)
return Utils.TrimName(values[1]);
if (values2[0] == "skinpack")
return Utils.TrimName(values[1]);
}
if (values[0].Contains(name))
return Utils.TrimName(values[1]);
}
if (name.Contains("."))
return Utils.TrimName(defaultName);
else
return Utils.TrimName(name);
}
else
return Utils.TrimName(defaultName);
}
}
return Utils.TrimName(defaultName);
}
public static string ReadUUID(string manifestPath)
{
dynamic manifest = JsonConvert.DeserializeObject(File.ReadAllText(manifestPath));
return manifest.header.uuid.ToString();
}
public static void ChangeUUID(string manifestPath, string newUUID)
{
dynamic manifest = JsonConvert.DeserializeObject(File.ReadAllText(manifestPath));
manifest.header.uuid = newUUID;
File.WriteAllText(manifestPath, JsonConvert.SerializeObject(manifest));
}
}
}

329
McCrypt/Marketplace.cs Normal file
View file

@ -0,0 +1,329 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Threading;
namespace McCrypt
{
public class Marketplace
{
// Hi mojang <3
// https://www.youtube.com/watch?v=jIM6dN3ogbk
// https://www.youtube.com/watch?v=mnnYCJNhw7w
private static string[] dontEncrypt = new string[] { "manifest.json", "contents.json", "texts", "pack_icon.png", "ui"};
private struct contentsJson
{
public int version;
public List<Object> content;
}
private struct contentKeys
{
public string key;
public string path;
}
private struct content
{
public string path;
}
// Checks if file is inside the filename blacklist
private static bool shouldEncrypt(string relPath)
{
foreach (string part in relPath.Split('/'))
if (dontEncrypt.Contains(part))
return false;
return true;
}
// Removes "prid" NBT tag
// This is the one that makes the game tell you to plz buy the pack
public static void CrackLevelDat(string levelDatFile)
{
byte[] find = Encoding.UTF8.GetBytes("prid"); // bytes to locate
byte[] leveldat = File.ReadAllBytes(levelDatFile); // read level.dat
Int64 location = Utils.FindData(leveldat, find); // locate where "prid" is inside level.dat
if (location != -1)
{
FileStream fs = File.Open(levelDatFile, FileMode.Open, FileAccess.ReadWrite); // Open the file for writing
fs.Seek(location + 3, SeekOrigin.Begin);
fs.WriteByte((byte)'a'); // Change to "pria" which the game will just ignore
fs.Close();
}
}
// Change all skins type to "free" instead of "paid"
// This makes the game let you actually apply them
public static void CrackSkinsJson(string skinsJsonFile)
{
File.WriteAllText(skinsJsonFile, File.ReadAllText(skinsJsonFile).Replace("\"paid\"", "\"free\"")); // Read file, replace all "paid" with "free", write file back
}
// Extract a zipe file to the folder its contained in
// And delete the zipe file.
public static void CrackZipe(string zipeFile)
{
ZipFile.ExtractToDirectory(zipeFile, Path.GetDirectoryName(zipeFile));
File.Delete(zipeFile);
}
// EncryptContents Overload to generate keys
public static string EncryptContents(string basePath, string uuid)
{
return EncryptContents(basePath, uuid, Keys.GenerateKey());
}
// Encrypts a contents.json and all files in it-
public static string EncryptContents(string basePath, string uuid, string ContentKey)
{
string contentsJsonPath = Path.Combine(basePath, "contents.json"); // Path to contents.json
contentsJson contentsJson = new contentsJson();
contentsJson.version = 1;
contentsJson.content = new List<object>();
foreach (string entry in Directory.GetFileSystemEntries(basePath, "*", SearchOption.AllDirectories))
{
string relPath = entry.Remove(0, basePath.Length + 1); // Get path relative to pack folder
relPath = relPath.Replace("\\", "/"); // Replace Windows-Style paths, with UNIX paths
bool shouldEnc = shouldEncrypt(relPath);
if (Utils.IsDirectory(entry)) // If its a directroy, add "/" to the end to signify this
{
relPath += "/";
shouldEnc = false;
}
if (shouldEnc) // Check file is not blacklisted file
{
contentKeys keys = new contentKeys();
keys.path = relPath;
keys.key = Keys.GenerateKey(); // Generate a random key for this file
byte[] key = Encoding.UTF8.GetBytes(keys.key); // Copy first 16 bytes of key as IV
byte[] iv = new byte[16];
Array.Copy(key, iv, 16);
byte[] encryptedData = Crypto.Aes256CfbEncrypt(key, iv, File.ReadAllBytes(entry)); // Encrypt the file
File.WriteAllBytes(entry, encryptedData); // Write file
contentsJson.content.Add(keys); // add to content list
}
else // Just add to the content list without encrypting it
{
content content = new content();
content.path = relPath;
contentsJson.content.Add(content);
}
}
string json = JsonConvert.SerializeObject(contentsJson); // JSON Encode contents.json
byte[] contentKey = Encoding.UTF8.GetBytes(ContentKey); // Copy first 16 bytes of the key for IV
byte[] contentIv = new byte[16];
Array.Copy(contentKey, contentIv, 16);
byte[] encryptedJson = Crypto.Aes256CfbEncrypt(contentKey, contentIv, Encoding.UTF8.GetBytes(json)); // Encrypt JSON
// Create encrypted file w header
FileStream fs = File.OpenWrite(contentsJsonPath);
BinaryWriter bw = new BinaryWriter(fs);
bw.Write((uint)0);
bw.Write((uint)0x9BCFB9FC);
bw.Write((UInt64)0);
fs.WriteByte((byte)uuid.Length);
Utils.WriteString(fs, uuid, 0xEF);
fs.Write(encryptedJson, 0, encryptedJson.Length);
fs.Close();
return ContentKey;
}
// Decrypt encrypted entitlement buffer using a specified ent key
internal static byte[] decryptEntitlementBuffer(byte[] EntCiphertext, byte[] EntKey)
{
byte[] iv = new byte[16];
Array.Copy(EntKey, iv, iv.Length);
return Crypto.Aes256CfbDecrypt(EntKey, iv, EntCiphertext);
}
// Decrypt a world (leveldb) or contents.json file
// For the file types that have a header-
private static byte[] worldOrContentsJsonDecrypt(string file)
{
FileStream fs = File.OpenRead(file); // Open file for reading
BinaryReader br = new BinaryReader(fs); // Create a binary reader overlay of it
if (fs.Length <= 0)
{
fs.Dispose();
return new byte[0] { };
}
uint version = br.ReadUInt32();
uint magic = br.ReadUInt32();
UInt64 unk = br.ReadUInt64();
if (version == 0 && magic == 0x9bcfb9fc) // is valid header?
{
int len = fs.ReadByte();
string uuid = Utils.ReadString(fs, len); // Read the pack UUID for this file
byte[] key = Keys.LookupKey(uuid); // Look for key inside .ent / keys.db
if (key == null)
key = Encoding.UTF8.GetBytes("s5s5ejuDru4uchuF2drUFuthaspAbepE"); // Generic skinpack key
// Every skinpack has the same key lol
// This might be wrong, but hey! if it works, it works :D
fs.Seek(0x100, SeekOrigin.Begin); // Read ciphertext
byte[] ciphertext = new byte[fs.Length - 0x100];
fs.Read(ciphertext, 0x00, ciphertext.Length);
byte[] iv = new byte[16]; // Copy first 16 bytes of Key for IV
Array.Copy(key, iv, iv.Length);
byte[] plaintext = Crypto.Aes256CfbDecrypt(key, iv, ciphertext); // Decrypt data
fs.Dispose();
return plaintext;
}
else
{
fs.Dispose();
throw new InvalidDataException("Not a valid LEVELDB or CONTENTS.JSON file.");
}
}
// Read contents.json, and decrypt all files inside
// Now Multi-Threaded for speed!
private static void decryptContentsJsonFiles(string contentsJsonPath, List<Thread> threadList)
{
string baseDirectory = Path.GetDirectoryName(contentsJsonPath); // Get pack folder
string contentsJson = File.ReadAllText(contentsJsonPath); // Read contents.json
dynamic contentsJsonData = Utils.JsonDecodeCloserToMinecraft(contentsJson); // Parse contents.json
int totalContents = contentsJsonData.content.Count;
for (int i = 0; i < totalContents; i++)
{
string relPath = contentsJsonData.content[i].path; // Relative path to file to be decrypted
string decKey = contentsJsonData.content[i].key; // Key for file to be decrypted
if (decKey == null)
continue;
Thread thrd = new Thread(() =>
{
string filePath = Path.Combine(baseDirectory, relPath); // Combine pack dir, with file relative path
byte[] key = Encoding.UTF8.GetBytes(decKey); // Get key bytes
byte[] iv = new byte[16];
Array.Copy(key, iv, iv.Length); // Copy first 16 bytes of key as IV
byte[] cipherText = File.ReadAllBytes(filePath); // Read the file
byte[] plainText = Crypto.Aes256CfbDecrypt(key, iv, cipherText); // Decrypt the file
File.WriteAllBytes(filePath, plainText); // Write back decrypted filie
});
thrd.Priority = ThreadPriority.Highest;
threadList.Add(thrd);
thrd.Start();
}
}
// Decrypt an entire pack / world
// Recursively decrypts all sub-packs.
// Mutli-Threaded.
public static void DecryptContents(string contentsPath)
{
List<Thread> threadList = new List<Thread>();
string oldSchoolZipe = Path.Combine(contentsPath, "content.zipe");
string contentsJsonPath = Path.Combine(contentsPath, "contents.json");
if (File.Exists(oldSchoolZipe)) // Resource packs or Skin Packs
{
byte[] decryptedData = worldOrContentsJsonDecrypt(oldSchoolZipe); // Decrypt the zipe file
File.WriteAllBytes(oldSchoolZipe, decryptedData); // Write decrypted zip back to disk
}
else if (File.Exists(contentsJsonPath)) // Resource packs or Skin Packs
{
string subPacksFolder = Path.Combine(contentsPath, "subpacks");
byte[] decryptedData = worldOrContentsJsonDecrypt(contentsJsonPath); // Decrypt the contents.json file
File.WriteAllBytes(contentsJsonPath, decryptedData); // Write decrypted contents.json back to disk
decryptContentsJsonFiles(contentsJsonPath, threadList); // Decrypt all files in contents.json
// Decrypt all Sub-packs
if (Directory.Exists(subPacksFolder))
{
string[] subPacks = Directory.GetDirectories(subPacksFolder, "*", SearchOption.TopDirectoryOnly);
foreach (string subPack in subPacks)
DecryptContents(Path.Combine(subPacksFolder, subPack));
}
}
else // World Templates
{
string behaviourPacksFolder = Path.Combine(contentsPath, "behavior_packs"); // Get World Resource Packs folder
string resourcePacksFolder = Path.Combine(contentsPath, "resource_packs"); // Get World Behaviour Packs folder
string levelDbFolder = Path.Combine(contentsPath, "db"); // Get leveldb folder
// Decrypt all sub-behavour packs
if (Directory.Exists(behaviourPacksFolder))
{
string[] behaviourPacks = Directory.GetDirectories(behaviourPacksFolder, "*", SearchOption.TopDirectoryOnly);
foreach (string behaviourPack in behaviourPacks)
DecryptContents(Path.Combine(behaviourPacksFolder, behaviourPack));
}
// Decrypt all sub-resource packs
if (Directory.Exists(resourcePacksFolder))
{
string[] resourcePacks = Directory.GetDirectories(resourcePacksFolder, "*", SearchOption.TopDirectoryOnly);
foreach (string resourcePack in resourcePacks)
DecryptContents(Path.Combine(resourcePacksFolder, resourcePack));
}
// Decrypt leveldb files
if (Directory.Exists(levelDbFolder))
{
string[] levelDbFiles = Directory.GetFiles(levelDbFolder, "*", SearchOption.AllDirectories);
foreach (string levelDbFile in levelDbFiles)
{
Thread thrd = new Thread(() =>
{
string fileToDecrypt = Path.Combine(levelDbFolder, levelDbFile); // Get full path to leveldb file
byte[] decryptedData;
try
{
decryptedData = worldOrContentsJsonDecrypt(fileToDecrypt); // Decrypr file
File.WriteAllBytes(fileToDecrypt, decryptedData); // Write to disk
}
catch (InvalidDataException)
{
Console.Error.WriteLine("Failed to decrypt db/" + Path.GetFileName(levelDbFile));
}
});
thrd.Priority = ThreadPriority.Highest;
threadList.Add(thrd);
thrd.Start();
}
}
}
Thread[] threads = threadList.ToArray();
threadList.Clear();
foreach(Thread t in threads)
t.Join();
}
}
}

View file

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("McCrypt")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("McCrypt")]
[assembly: AssemblyCopyright("Copyright © 2022")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("4bef6f52-6545-4bb9-8053-50335a1c6789")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

97
McCrypt/Utils.cs Normal file
View file

@ -0,0 +1,97 @@
using Newtonsoft.Json;
using System;
using System.IO;
using System.Text;
namespace McCrypt
{
internal class Utils
{
internal static object JsonDecodeCloserToMinecraft(string json)
{
for (int i = json.Length; i > 0; i--)
{
try
{
return JsonConvert.DeserializeObject(json.Substring(0, i));
}
catch (Exception) { };
}
throw new Exception();
}
internal static bool IsDirectory(string path)
{
if (Directory.Exists(path))
return true;
else if (File.Exists(path))
return false;
else
throw new FileNotFoundException("Cannot find file: " + path);
}
internal static Int64 FindData(byte[] data, byte[] pattern)
{
for (Int64 i = 0; i < data.LongLength - pattern.LongLength; i++)
{
bool match = true;
for (Int64 k = 0; k < pattern.LongLength; k++)
{
if (data[i + k] != pattern[k])
{
match = false;
break;
}
}
if (match)
{
return i;
}
}
return -1;
}
internal static string ReadString(Stream str, int len)
{
byte[] stringBytes = new byte[len];
str.Read(stringBytes, 0x00, len);
return Encoding.UTF8.GetString(stringBytes);
}
internal static void WriteString(Stream stream, string str, long totalLength)
{
byte[] data = Encoding.UTF8.GetBytes(str);
long paddingLen = totalLength - data.Length;
byte[] padding = new byte[paddingLen];
stream.Write(data, 0, data.Length);
stream.Write(padding, 0, padding.Length);
}
internal static byte[] ForceDecodeBase64(string base64Data)
{
for (int i = 0; i < 20; i++)
{
try
{
return Convert.FromBase64String(base64Data);
}
catch (Exception)
{
base64Data += "=";
}
}
return null;
}
internal static string TrimName(string name)
{
if (name.Contains("#"))
{
return name.Substring(0, name.IndexOf("#")).Trim();
}
return name.Trim();
}
}
}

4
McCrypt/packages.config Normal file
View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net48" />
</packages>