mirror of
https://silica.codes/BedrockReverse/MCPackDecrypt.git
synced 2025-04-20 03:39:17 +12:00
Inital Commit:
This commit is contained in:
commit
8707e18fa9
16 changed files with 2242 additions and 0 deletions
197
packDecrypter.js
Normal file
197
packDecrypter.js
Normal file
|
@ -0,0 +1,197 @@
|
|||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const aescfb = require('./aes');
|
||||
const JSZip = require("jszip");
|
||||
const lookupKey = require("./ent");
|
||||
const Progress = require("./progress");
|
||||
const { ipcMain } = require("electron");
|
||||
|
||||
|
||||
module.exports = class PackDecryptor extends Progress {
|
||||
|
||||
inputPath = "";
|
||||
outputFilePath = "";
|
||||
zip = new JSZip();
|
||||
zippedContent = [];
|
||||
contentFiles = []
|
||||
|
||||
decryptDenylist = ["pack_icon.png", "pack_icon.jpeg", "world_icon.png", "world_icon.jpeg", "manifest.json"]
|
||||
|
||||
|
||||
constructor(inputPath, outputFilePath) {
|
||||
super();
|
||||
this.inputPath = inputPath;
|
||||
this.outputFilePath = outputFilePath;
|
||||
}
|
||||
async start() {
|
||||
return new Promise(async res => {
|
||||
console.log("start")
|
||||
const dbPath = path.join(this.inputPath, "db");
|
||||
this.contentFiles = recursiveReaddirrSync(this.inputPath);
|
||||
this._started = true;
|
||||
|
||||
if (fs.existsSync(dbPath)) {
|
||||
const dbDir = recursiveReaddirrSync(dbPath);
|
||||
|
||||
for (let index = 0; index < dbDir.length; index++) {
|
||||
const dbFilePath = dbDir[index];
|
||||
if (fs.lstatSync(dbFilePath).isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
const decrypted = await this.decryptContentFile(dbFilePath);
|
||||
this.addFile(dbFilePath, decrypted)
|
||||
}
|
||||
}
|
||||
|
||||
for (let index = 0; index < this.contentFiles.length; index++) {
|
||||
const name = path.basename(this.contentFiles[index]);
|
||||
if (name.toLowerCase() === "contents.json") {
|
||||
await this.decryptContent(this.contentFiles[index]);
|
||||
}
|
||||
}
|
||||
|
||||
for (let index = 0; index < this.contentFiles.length; index++) {
|
||||
const filePath = this.contentFiles[index];
|
||||
if (!this.zippedContent.includes(filePath)) {
|
||||
this.addFile(filePath, fs.readFileSync(filePath));
|
||||
}
|
||||
}
|
||||
|
||||
await this.crackSkinPack()
|
||||
this.crackWorld();
|
||||
|
||||
|
||||
this.zip.generateAsync({type:"arraybuffer"}).then((content) => {
|
||||
fs.writeFileSync(this.outputFilePath, Buffer.from(content));
|
||||
console.log("done")
|
||||
res(0);
|
||||
});
|
||||
})
|
||||
}
|
||||
crackWorld() {
|
||||
const levelDatPath = path.join(this.inputPath, "level.dat");
|
||||
if (!fs.existsSync(levelDatPath)) return;
|
||||
const levelDat = fs.readFileSync(levelDatPath);
|
||||
|
||||
|
||||
let offset = levelDat.indexOf("prid");
|
||||
while (offset !== -1) {
|
||||
levelDat.writeUInt8("a".charCodeAt(0), offset);
|
||||
offset = levelDat.indexOf("prid");
|
||||
}
|
||||
|
||||
this.addFile(levelDatPath, levelDat)
|
||||
}
|
||||
|
||||
addFile(filePath, content) {
|
||||
const relPath = path.relative(this.inputPath, filePath).replaceAll("\\", "/");
|
||||
if (!this.zippedContent.includes(filePath)) {
|
||||
this.zippedContent.push(filePath);
|
||||
}
|
||||
this.zip.file(relPath, content, {binary: true})
|
||||
|
||||
self.postMessage(this.getPercentage());
|
||||
|
||||
}
|
||||
|
||||
|
||||
static isContentFileEncrypted(filePath){
|
||||
const contents = fs.readFileSync(filePath);
|
||||
if (contents.length < 0x100) return false;
|
||||
const magic = contents.readUint32LE(0x4);
|
||||
if (magic === 2614082044) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async decryptContent(filePath) {
|
||||
const dirname = path.dirname(filePath);
|
||||
const isEncrypted = PackDecryptor.isContentFileEncrypted(filePath);
|
||||
const content = await this.decryptContentFile(filePath);
|
||||
const parsedContent = JSON.parse(content);
|
||||
|
||||
if(isEncrypted) {
|
||||
for (let index = 0; index < parsedContent.content.length; index++) {
|
||||
const key = parsedContent.content[index].key;
|
||||
const filePath = parsedContent.content[index].path;
|
||||
const fileName = path.basename(filePath);
|
||||
|
||||
if(this.decryptDenylist.indexOf(fileName.toLowerCase()) !== -1) continue;
|
||||
if (!key) continue;
|
||||
|
||||
const joinedPath = path.join(dirname, filePath);
|
||||
const file = await this.decryptFile(joinedPath, key);
|
||||
this.addFile(joinedPath, file)
|
||||
}
|
||||
}
|
||||
|
||||
this.addFile(filePath, content)
|
||||
}
|
||||
|
||||
async decryptContentFile(filePath) {
|
||||
const contents = fs.readFileSync(filePath);
|
||||
if (contents.length < 0x100) return contents;
|
||||
const magic = contents.readUint32LE(0x4);
|
||||
if (magic === 2614082044) {
|
||||
const cipherText = contents.subarray(0x100);
|
||||
const uuidSize = contents.readUInt8(0x10)
|
||||
const uuid = contents.subarray(0x11, 0x11 + uuidSize)
|
||||
const key = lookupKey(uuid);
|
||||
|
||||
const decrypted = decryptAes(key, cipherText)
|
||||
return decrypted
|
||||
} else {
|
||||
return contents;
|
||||
}
|
||||
}
|
||||
|
||||
async decryptFile(filePath, key) {
|
||||
const contents = fs.readFileSync(filePath);
|
||||
|
||||
const decrypted = decryptAes(key, contents)
|
||||
return decrypted;
|
||||
}
|
||||
|
||||
async crackSkinPack() {
|
||||
const skinJsonFilePath = "skins.json";
|
||||
|
||||
if (!this.zip.files[skinJsonFilePath]) return;
|
||||
const skinsFile = await this.zip.files[skinJsonFilePath].async("string");
|
||||
|
||||
try{
|
||||
const skins = JSON.parse(skinsFile);
|
||||
|
||||
for (let index = 0; index < skins.skins.length; index++) {
|
||||
const skin = skins.skins[index];
|
||||
skin.type = "free";
|
||||
}
|
||||
|
||||
this.addFile(path.join(this.inputPath, skinJsonFilePath), JSON.stringify(skins, null, 2));
|
||||
}
|
||||
catch(Exception) {};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function recursiveReaddirrSync(dir) {
|
||||
let results = [];
|
||||
let list = fs.readdirSync(dir);
|
||||
list.forEach(function (file) {
|
||||
file = path.join(dir, file);
|
||||
let stat = fs.statSync(file);
|
||||
if (stat && stat.isDirectory()) {
|
||||
results = results.concat(recursiveReaddirrSync(file));
|
||||
} else {
|
||||
results.push(file);
|
||||
}
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
function decryptAes(key, buffer) {
|
||||
const bufferKey = Buffer.from(key, 'binary');
|
||||
|
||||
return aescfb(buffer, bufferKey);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue