de4js
Version:
JavaScript Deobfuscator and Unpacker
474 lines (413 loc) • 13.3 kB
JavaScript
---
---
/**
* @name {{ site.name }}
* @description {{ site.description }}
* @author {{ site.author }} <{{ site.author_email }}> ({{ site.url }})
* @version {{ site.version }}
* @copyright {{ site.author }} 2017
* @license {{ site.license }}
*/
/* globals ClipboardJS */
(function () {
// https://davidwalsh.name/javascript-debounce-function
function debounce(func, wait, immediate) {
var timeout;
return function () {
var context = this,
args = arguments;
var later = function () {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
}
// https://stackoverflow.com/a/28318964
function parseFile(file, chunk, done) {
var fileSize = file.size,
chunkSize = 64 * 1024,
offset = 0,
chunkReaderBlock = null,
readEventHandler = function (evt) {
if (evt.target.error == null) {
chunk(evt.target.result);
} else {
return;
}
if (offset >= fileSize) {
done();
return;
}
offset += chunkSize;
chunkReaderBlock(offset, chunkSize, file);
};
chunkReaderBlock = function (_offset, length, _file) {
var r = new FileReader();
var blob = _file.slice(_offset, length + _offset);
r.onload = readEventHandler;
r.readAsText(blob);
};
chunkReaderBlock(offset, chunkSize, file);
}
function updateOnlineStatus() {
if (navigator.onLine) {
title.classList.remove('offline');
urlRemove.disabled = false;
submitRemove.disabled = false;
} else {
title.classList.add('offline');
urlRemove.disabled = true;
submitRemove.disabled = true;
}
}
var wrapper = document.getElementById('main_content'),
input = document.getElementById('input'),
file = document.getElementById('file'),
fileName = document.getElementById('fileName'),
contentLocal = document.getElementById('contentLocal'),
renderLocal = document.getElementById('renderLocal'),
formRemove = document.getElementById('formRemove'),
urlRemove = document.getElementById('urlRemove'),
submitRemove = document.getElementById('submitRemove'),
renderRemove = document.getElementById('renderRemove'),
view = document.getElementById('view'),
encode = document.getElementsByName('encode'),
options,
none = document.getElementById('none'),
readable = document.getElementById('readable'),
form = document.de4js,
packer = form.encode.value,
temp = '',
disableAll = function (check) {
for (var i = 0; i < encode.length; i++) {
if (encode[i].value === 'nicify') continue;
encode[i].disabled = check;
}
},
clear = document.getElementById('clear'),
autoBtn = document.getElementById('auto'),
isAuto = false,
preview = document.getElementById('preview'),
download = document.getElementById('download'),
clipboard = new ClipboardJS('#copyjs', {
target: function () {
return view;
},
}),
title = document.getElementById('title'),
startEffect = function () {
view.textContent = '';
view.classList.add('waiting');
clear.disabled = true;
autoBtn.disabled = true;
disableAll(true);
options = Array.from(document.querySelectorAll('.de4js-option')).reduce((obj, e) => {
obj[e.name] = e.checked;
return obj;
}, {});
},
stopEffect = function () {
isAuto = false;
view.classList.remove('waiting');
clear.disabled = false;
autoBtn.disabled = false;
setTimeout(function () {
disableAll(false);
}, 0);
},
resetCopy = function (trigger) {
if (!trigger.classList.contains('copied')) return;
trigger.classList.remove('copied');
},
timeReset = function (trigger) {
setTimeout(function () {
resetCopy(trigger);
}, 800);
},
externalStyle =
'*{margin:0;padding:0}html{line-height:1em;background:#1d1f21;color:#c5c8c6}pre{white-space:pre-wrap;word-wrap:break-word;word-break:break-all}{% include highlight-js/styles/hljs-theme.css %}{% include highlight-js/styles/hljs-line-numbers.css %}html,body,.hljs{background:#030303}',
externalUrl,
externalPreview = function (source) {
if (externalUrl) URL.revokeObjectURL(externalUrl);
source =
'<html><head><meta charset="utf-8"><link rel="shortcut icon" type="image/png" href="{{ site.url }}{{ site.baseurl }}{{ "/favicon.png" }}"><title>{{ site.name }} | Preview</title><style>' +
externalStyle +
'</style></head><body><pre class="hljs">' +
source +
'</pre></body></html>';
externalUrl = new Blob([source], {
type: 'text/html',
});
externalUrl = URL.createObjectURL(externalUrl);
preview.classList.add('show');
preview.href = externalUrl;
},
downloadUrl,
downloadResult = function (source) {
if (downloadUrl) URL.revokeObjectURL(downloadUrl);
downloadUrl = new Blob([source], {
type: 'text/javascript',
});
downloadUrl = URL.createObjectURL(downloadUrl);
download.classList.add('show');
download.href = downloadUrl;
},
workerFormat,
workerDecode,
workerError = (err) => {
stopEffect();
view.innerHTML = `<span class="hljs-variable">${err.message}</span>`;
},
format = debounce(function () {
if (temp === '') return;
if (!workerFormat) {
workerFormat = new Worker('{{ "worker/format.js" | relative_url }}');
workerFormat.addEventListener('message', function (e) {
if (!e.data.highlight) {
downloadResult(e.data.result);
return;
}
view.innerHTML = e.data.result;
externalPreview(e.data.result);
stopEffect();
});
}
startEffect();
workerFormat.postMessage({
source: temp,
options: options,
});
workerFormat.addEventListener('error', workerError);
}, 250),
detect = function (source) {
var type = '';
if (/^var\s_\d{4};[\s\n]*var\s_\d{4}\s?=/.test(source)) {
type = '_numberencode';
} else if (source.indexOf("/`m´)ノ ~┻━┻ //*´∇`*/ ['_'];") !== -1) {
// eslint-disable-line quotes
type = 'aaencode';
} else if (source.indexOf('$={___:++$,$$$$:(![]+"")[$]') !== -1) {
type = 'jjencode';
} else if (source.replace(/[[\]()!+]/gm, '').trim() === '') {
type = 'jsfuck';
} else if (
source.indexOf(' ') === -1 &&
(source.indexOf('%2') !== -1 || source.replace(/[^%]+/g, '').length > 3)
) {
type = 'urlencode';
} else if (
/((?![^_a-zA-Z$])[\w$]*)\(-?('|")(0x[a-f\d]+|\\x30\\x78[\\xa-f\d]+)\2(\s*,\s*('|").+?\5)?\)/i.test(source)
) {
type = 'obfuscatorio';
} else if (/^var\s+((?![^_a-zA-Z$])[\w$]*)\s*=\s*\[.*?\];/.test(source)) {
type = 'arrayencode';
} else if (
source.startsWith('//Protected by WiseLoop PHP JavaScript Obfuscator') ||
source.includes(';eval(function(w,i,s,e)')
) {
type = 'wiseloop';
} else if (source.indexOf('eval(') !== -1) {
if (/\b(window|document|console)\.\b/i.test(source)) return type;
type = 'evalencode';
}
document.querySelector('.magic-radio:checked').checked = false;
document.querySelector('.magic-radio[value="' + type + '"]').checked = true;
return type;
},
decode = debounce(function () {
if (temp === '') temp = input.value.trim();
temp = temp.replace(/\/\*(?!\s*@de4js)[\s\S]*?\*\/|^[\s\t]*\/\/.*/gm, '');
if (temp === '') return;
packer = isAuto ? detect(temp) : form.encode.value;
if (packer === 'nicify') return;
if (packer === '') {
format();
return;
}
if (!workerDecode) {
workerDecode = new Worker('{{ "worker/decode.js" | relative_url }}');
workerDecode.addEventListener('message', function (e) {
if (e.data !== temp) {
temp = e.data;
if (isAuto) {
decode();
return;
}
}
format();
});
workerDecode.addEventListener('error', workerError);
}
startEffect();
workerDecode.postMessage({
source: temp,
packer: packer,
options: options,
});
}, 250),
changeEncode = function (e) {
var _this = e.target;
if (_this.name !== 'encode') return;
decode();
},
dragEnd = function () {
contentLocal.classList.remove('drop-zone', 'drop-enter');
},
uploadFile = function (fileObj) {
if (!fileObj) return;
var fragment = new DocumentFragment();
fileName.textContent = fileObj.name;
if (!/((text|application)\/(ecmascript|(x-)?javascript)|text\/plain)/.test(fileObj.type)) {
renderLocal.textContent = 'Invalid file type';
return;
}
temp = '';
renderLocal.textContent = '';
parseFile(
fileObj,
function (data) {
temp += data;
var txt = document.createTextNode(data);
fragment.appendChild(txt);
},
function () {
decode();
renderLocal.appendChild(fragment);
file.value = '';
},
);
};
input.oninput = function () {
temp = input.value.trim();
decode();
};
form.addEventListener('change', changeEncode);
form.addEventListener('click', changeEncode);
autoBtn.onclick = function () {
isAuto = true;
decode();
};
clipboard.on('success', function (e) {
e.trigger.classList.add('copied');
e.clearSelection();
timeReset(e.trigger);
});
clipboard.on('error', function (e) {
e.trigger.classList.add('selected');
timeReset(e.trigger);
});
clear.onclick = function () {
view.textContent = '';
file.value = '';
renderLocal.textContent = '';
dragEnd();
fileName.textContent = '';
renderRemove.textContent = '';
urlRemove.value = '';
none.click();
stopEffect();
setTimeout(function () {
input.value = '';
temp = '';
}, 0);
if (workerDecode) {
workerDecode.terminate();
workerDecode = undefined;
}
if (workerFormat) {
workerFormat.terminate();
workerFormat = undefined;
}
preview.classList.remove('show');
};
window.addEventListener('online', updateOnlineStatus);
window.addEventListener('offline', updateOnlineStatus);
updateOnlineStatus();
// Tabs control
wrapper.addEventListener('click', function (e) {
var _this = e.target;
if (!_this.classList.contains('tab')) return;
clear.click();
wrapper.querySelector('.tab.active').classList.remove('active');
_this.classList.add('active');
wrapper.querySelector('.tab-content.show').classList.remove('show');
wrapper.querySelector('#content' + _this.dataset.target).classList.add('show');
});
file.onchange = function () {
uploadFile(this.files[0]);
};
file.onfocus = function () {
this.classList.add('has-focus');
};
file.onblur = function () {
this.classList.remove('has-focus');
};
document.ondrop = function (e) {
e.preventDefault();
dragEnd();
if (e.target.id !== 'contentLocal') return;
uploadFile(e.dataTransfer.files[0]);
};
document.ondragover = function (e) {
e.preventDefault();
contentLocal.classList.add('drop-zone');
};
document.ondragend = function (e) {
e.preventDefault();
dragEnd();
};
document.onkeyup = function (e) {
if (e.keyCode !== 27) return;
dragEnd();
};
contentLocal.onclick = function () {
if (!contentLocal.classList.contains('drop-zone')) return;
dragEnd();
};
contentLocal.ondragenter = function (e) {
e.preventDefault();
this.classList.add('drop-enter');
};
contentLocal.ondragleave = function (e) {
e.preventDefault();
this.classList.remove('drop-enter');
};
formRemove.onsubmit = function (e) {
e.preventDefault();
var fragment = new DocumentFragment(),
url = urlRemove.value;
temp = '';
renderRemove.textContent = '';
fetch(url)
.then(function (res) {
if (!res.ok) {
throw Error(res.statusText);
}
if (
res.headers.get('content-type').search(/((text|application)\/(ecmascript|(x-)?javascript)|text\/plain)/i) ===
-1
) {
throw Error('Invalid file type');
}
return res.text();
})
.then(function (data) {
temp = data;
decode();
var txt = document.createTextNode(data);
fragment.appendChild(txt);
renderRemove.appendChild(fragment);
})
.catch(function (err) {
renderRemove.innerHTML = `<span class="hljs-variable">${err.message}</span>`;
});
};
readable.onchange = function () {
temp = readable.value;
format();
};
})();