Skip to content

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
v1.0 新增被动解密操作
  • Loading branch information
HTMonkeyG authored Oct 19, 2023
1 parent 0b912c0 commit df9ae00
Show file tree
Hide file tree
Showing 4 changed files with 336 additions and 0 deletions.
41 changes: 41 additions & 0 deletions XOREncryptHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
function checkFileIsEncrypt(buf){
if(buf.readUInt32LE && (buf.readUInt32BE(0) == 0x801D3001 || buf.readUInt32BE(0) == 0x901D3001)) return !0;
else return !1
}

function encryptFile(buf, key){
if(key.length != 8) return !1;
if(!key || !key.length)
key = [ 0x31, 0x35, 0x38, 0x39, 0x32, 0x33, 0x38, 0x38 ];

var ret = Buffer.from(buf),
magicNum = Buffer.from([0x80, 0x1D, 0x30, 0x01]);

for(var i = 0;i < ret.length;i++)
ret[i] ^= key[i % 8];

ret = Buffer.concat(magicNum, ret);

return ret;
}

function decryptFile(buf, key){
if(!key || !key.length)
key = Buffer.from('88329851');
else key = Buffer.from(key);

var ret = Buffer.from(buf);

if(!checkFileIsEncrypt(buf)) return !1;

ret = ret.subarray(4);

for(var i = 0;i < ret.length;i++)
ret[i] ^= key[i % key.length];

return ret;
}

exports.checkFileIsEncrypt = checkFileIsEncrypt,
exports.decryptFile = decryptFile,
exports.encryptFile = encryptFile;
179 changes: 179 additions & 0 deletions main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/****************************************
*
* * XOR (eXtra Octopus Resistor) MC
* Netease Archive Encrypt & Decrypt
* Helper
*
* * By HTMonkeyG
*
****************************************/


/* Imports */
const fs = require('fs'),
fmt = require('./tFormat.js'),
rl = require('readline'),
pathLib = require('path'),
texts = require('./text.js'),
XOR = require('./XOREncryptHelper.js');

/* Folder Copy Function */
function copyFolderSync(source, target, a){
if (!fs.existsSync(target))
fs.mkdirSync(target);
const ls = fs.readdirSync(source);
ls.forEach((file) => {
const sP = pathLib.join(source, file);
const tP = pathLib.join(target, file);
const st = fs.statSync(sP);
if (st.isFile()) {
a(sP),
fs.copyFileSync(sP, tP);
} else if (st.isDirectory()) {
copyFolderSync(sP, tP, a);
}
});
}

function delFolderSync(target){
if(fs.existsSync(target)){
var ls = fs.readdirSync(target);
ls.forEach((a) => {
var p = pathLib.join(target, a);
if(fs.statSync(p).isDirectory())
delFolderSync(p);
else
fs.unlinkSync(p);
});
fs.rmdirSync(target);
}
}

/* Welcome Text */
console.log(texts.log.welcome);
console.log(texts.log.tip1);

var state = 0,
path = '';

var UI = rl.createInterface({
input: process.stdin,
output: process.stdout,
prompt: '\x1b[0m> ',
});

UI.prompt();

UI.on('line', function(e){
switch(state){
case 0:
path = e.trim();
path = pathLib.resolve('', path);
fmt.printF(texts.log.targetPath, [ path ]);
try{
if(!fs.statSync(path).isDirectory()){
fmt.printF(texts.log.notDir);
} else {
fmt.printF(texts.log.test);
if(!avalTest(path, function(a){
fmt.printF(texts.log.missing, [ a ])
}))
fmt.printF(texts.log.testFail);
else
state = 1,
fmt.printF(texts.log.testSucc);
}
} catch(e){
fmt.printF(texts.log.targetNotExist)
}
break;
case 1:
var op = e.trim();
op = Number(op);
switch(op){
case 0:
defaultDecrypt(),
state = 0;
console.log(texts.log.tip1);
break;
default:
fmt.printF(texts.log.invalidOp);
break;
}
break;
}
UI.prompt();
}).on('close', function(){
fmt.printF(texts.log.exit);
process.exit(0);
});

function defaultDecrypt(){
fmt.printF(texts.log.tip2)
var lPath = pathLib.join(path, 'db'),
ls = fs.readdirSync(lPath),
encrypted = [];

ls.forEach(function(a){
var tempBuf = fs.readFileSync(pathLib.join(lPath, a));
XOR.checkFileIsEncrypt(tempBuf) ? encrypted.push(a) : 0;
});

if(!encrypted.length){
fmt.printF(texts.log.noEncFile);
return
}
if(encrypted.length <= 3)
fmt.printF(texts.log.le3EncFile, [ encrypted.join(', ') ]);
else
fmt.printF(texts.log.gt3EncFile, [ encrypted.slice(0, 3).join(', '), encrypted.length ]);

fmt.printF(texts.log.createFolder);
var name = pathLib.basename(path) + '_Dec',
resultPath = pathLib.join(path, '..', name);
try {
fmt.printF(texts.log.cpFile);
copyFolderSync(path, resultPath, (a) => {
fmt.printF(texts.log.cping, [ pathLib.relative(path, a) ])
})
encrypted.forEach((a)=>{
fmt.printF(texts.log.decrypting, [a]);

var buf = XOR.decryptFile(fs.readFileSync(pathLib.join(path, 'db', a)));
if(buf)
fs.writeFileSync(pathLib.join(resultPath, 'db', a), buf);
else
throw new Error('Unknown Decrypt Error');
});
fmt.printF(texts.log.decSucc1, [ resultPath ]);
} catch(e) {
fmt.printF(texts.log.error, [ e.message ]);
delFolderSync(resultPath)
}
}

function avalTest(path, log){
var ls = fs.readdirSync(path),
pass = !0;

if(ls.indexOf('level.dat') == -1)
pass = !1,
log('level.dat');

if(ls.indexOf('db') == -1 || !fs.statSync(path + '/db').isDirectory())
pass = !1,
log('db/');
else {
ls = fs.readdirSync(path + '/db');
var flag = 0x3;
for(var i = 0;i < ls.length;i++){
if(/^MANIFEST/.test(ls[i])) flag &= 0x6;
if(/^CURRENT/.test(ls[i])) flag &= 0x5;
if(!flag) break;
}
(flag & 0x1) ? (log('db/MANIFEST-*'), pass = !1) : 0;
(flag & 0x2) ? (log('db/CURRENT'), pass = !1) : 0;
}

return pass;
}
91 changes: 91 additions & 0 deletions tFormat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
function rgb(r, g, b){
return `\x1b[38;2;${r};${g};${b};m`
}

function mcFmtLog(txt){
console.log(mcFmtCode(txt))
}

function printF(txt, arr){
var ret = '';
if(txt == void 0 || !txt.length) return '';
for(var i = 0;i < txt.length;i++){
if(txt[i] == '\\')
(txt[i + 1] == void 0) ? ret += '\\' : (ret += txt[i+1], i++);
else if(txt[i] == '%')
(txt[i + 1] == void 0) ? ret += '%' : (ret += (arr[txt[i + 1]] || ''), i++);
else
ret += txt[i]
}
ret = mcFmtCode(ret),
console.log(ret);
}

function mcFmtCode(txt){
function code2console(a){
switch(a){
case '0':
return '\x1b[30m';
case '1':
return '\x1b[34m';
case '2':
return '\x1b[32m';
case '3':
return '\x1b[36m';
case '4':
return '\x1b[31m';
case '5':
return '\x1b[35m';
case '6':
return '\x1b[33m';
case '7':
return '\x1b[37m';
case '8':
return '\x1b[90m';
case '9':
return '\x1b[94m';
case 'a':
return '\x1b[92m';
case 'b':
return '\x1b[96m';
case 'c':
return '\x1b[91m';
case 'd':
return '\x1b[95m';
case 'e':
return '\x1b[93m';
case 'f':
return '\x1b[97m';
case 'g':
return '\x1b[38;2;221;214;5;m';
default:
return '';
}
}
var ret = '';
if(!txt.length) return '';
for(var i = 0;i < txt.length;i++){
if(txt[i] == '§')
(txt[i + 1] == void 0) ? ret += '§' : (ret += code2console(txt[i + 1]), i++ );
else
ret += txt[i]
}
return ret
}

function fmtDate(date) {
var d = new Date(date);
var Y = date.getFullYear();
var M = (date.getMonth()+1 < 10 ? '0'+(date.getMonth()+1):date.getMonth()+1);
var D = (date.getDate()<10 ? '0'+date.getDate():date.getDate());
var h = (date.getHours()<10 ? '0'+date.getHours():date.getHours());
var m = (date.getMinutes()<10 ? '0'+date.getMinutes():date.getMinutes());
var s = date.getSeconds()<10 ? '0'+date.getSeconds():date.getSeconds();
return ''+Y+M+D+h+m+s;
}

exports.rgb = rgb,
exports.fmtDate = fmtDate,
exports.printF = printF,
exports.mcFmtCode = mcFmtCode,
exports.mcFmtLog = mcFmtLog;
25 changes: 25 additions & 0 deletions text.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
var log = {
welcome: '\x1b[38;2;119;109;134;m欢迎使用XOR存档加解密工具v1.0.',
exit: '\n§d正在关闭软件...',
tip1: '\x1b[38;2;119;109;134;m请输入目标存档level.dat所在文件夹路径',
targetPath: '\x1b[38;2;119;109;134;m目标目录: %0',
notDir: '§4目标不是文件夹, 请重试.',
test: '§2已确认目标, 正在检测完整性...',
missing: '§4缺失: %0',
testSucc: '§2目标文件夹完整.\n\x1b[38;2;119;109;134;m请选择操作类型: 0.被动解密, 1.被动加密, 2.主动解密, 3.主动加密',
testFail: '§4完整性测试未通过, 请检查后再试.',
targetNotExist: '§4目标不存在.',
invalidOp: '§4无效的操作类型',
tip2: '\x1b[38;2;119;109;134;m正在进行被动解密.\n扫描文件夹...',
error: '§4出现错误: %0',
noEncFile: '§4未找到加密的文件.',
le3EncFile: '§2找到加密文件: §6%0',
gt3EncFile: '§2找到 §6%0 §2等§6%1§2个加密文件',
createFolder: '§2正在创建文件夹...',
cpFile: '§2正在复制源文件...',
cping: '§6%0',
decrypting: '§2正在解密: §6%0',
decSucc1: '§2解密成功, 解密后存档已保存至§6%0'
};

exports.log = log;

0 comments on commit df9ae00

Please sign in to comment.