mirror of
https://github.com/two-torial/webpatcher.git
synced 2026-09-23 00:27:55 +03:00
Merge pull request #11 from beer-psi/feat/check-crackproof
feat: check for packed executables and give a friendlier message
This commit is contained in:
+120
-1
@@ -696,6 +696,117 @@ var loadPatch = function(_this, self, patcher) {
|
||||
self.successDiv.innerHTML = successStr + " loaded successfully!";
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the input is a PE executable, and if it is, parses the PE
|
||||
* just enough to see if it's packed with HyperTech CrackProof, by
|
||||
* checking if the first DLL import is "KeRnEl32.dLl".
|
||||
*
|
||||
* If there was any error parsing the executable or if it was packed,
|
||||
* this function returns a string. If there were no errors, this returns
|
||||
* undefined.
|
||||
*
|
||||
* @param {Uint8Array} dllFile
|
||||
* @returns {string | undefined}
|
||||
*/
|
||||
var checkUnpackedExecutable = function(dllFile) {
|
||||
// Note to anyone looking: all offsets are absolute file offsets.
|
||||
|
||||
const DOS_EXECUTABLE_MAGIC = [0x4D, 0x5A];
|
||||
const PE_EXECUTABLE_MAGIC = [0x50, 0x45, 0x00, 0x00];
|
||||
const PE_ROM_MAGIC = 0x107;
|
||||
const PE32_MAGIC = 0x10B;
|
||||
const PE32_PLUS_MAGIC = 0x20B;
|
||||
const OFFSET_IMAGE_OPTIONAL_HEADER32_NUMBER_OF_RVAS = 0x5C;
|
||||
const OFFSET_IMAGE_OPTIONAL_HEADER64_NUMBER_OF_RVAS = 0x6C;
|
||||
const SIZEOF_IMAGE_IMPORT_DESCRIPTOR = 0x28;
|
||||
const CRACKPROOF_KERNEL32_DLL = [
|
||||
0x4B, 0x65, 0x52, 0x6E, 0x45, 0x6C, 0x33, 0x32, 0x2E, 0x64, 0x4C, 0x6C, 0x00
|
||||
]; // "KeRnEl32.dLl\0"
|
||||
|
||||
// We do have patchers for other types of files, like chu.acf,
|
||||
// so skip the check if we're not looking at a PE executable.
|
||||
if (!bytesMatch(dllFile, 0x00, DOS_EXECUTABLE_MAGIC)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const dllFileView = new DataView(dllFile.buffer);
|
||||
const peHeaderOffset = dllFileView.getUint32(0x3C, true); // e_lfanew
|
||||
|
||||
if (!bytesMatch(dllFile, peHeaderOffset, PE_EXECUTABLE_MAGIC)) {
|
||||
// Probably a file that accidentally looks like a DOS executable...
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const numberOfSections = dllFileView.getUint16(peHeaderOffset + 0x06, true);
|
||||
const sizeOfOptionalHeader = dllFileView.getUint16(peHeaderOffset + 0x14, true);
|
||||
const startOfOptionalHeader = peHeaderOffset + 0x18;
|
||||
const startOfSections = startOfOptionalHeader + sizeOfOptionalHeader;
|
||||
|
||||
const rva2offset = (rva) => {
|
||||
for (let i = 0; i < numberOfSections; i++) {
|
||||
const sectionHeaderOffset = startOfSections + 0x28 * i;
|
||||
const virtualSize = dllFileView.getUint32(sectionHeaderOffset + 0x08, true);
|
||||
const virtualAddress = dllFileView.getUint32(sectionHeaderOffset + 0x0C, true);
|
||||
const pointerToRawData = dllFileView.getUint32(sectionHeaderOffset + 0x14, true);
|
||||
|
||||
if (virtualAddress <= rva && (virtualAddress + virtualSize) > rva) {
|
||||
return rva - virtualAddress + pointerToRawData;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
};
|
||||
|
||||
const magic = dllFileView.getUint16(startOfOptionalHeader, true);
|
||||
|
||||
if (![PE_ROM_MAGIC, PE32_MAGIC, PE32_PLUS_MAGIC].includes(magic)) {
|
||||
return `Unknown PE format ${magic}.`;
|
||||
}
|
||||
|
||||
const numberOfRvaAndSizesOffset = startOfOptionalHeader + (
|
||||
magic === PE32_PLUS_MAGIC
|
||||
? OFFSET_IMAGE_OPTIONAL_HEADER64_NUMBER_OF_RVAS
|
||||
: OFFSET_IMAGE_OPTIONAL_HEADER32_NUMBER_OF_RVAS
|
||||
);
|
||||
const numberOfRvaAndSizes = dllFileView.getUint32(numberOfRvaAndSizesOffset, true);
|
||||
|
||||
// No import directory, apparently. Not our problem then.
|
||||
if (numberOfRvaAndSizes < 2) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const startOfDataDirectories = numberOfRvaAndSizesOffset + 0x04;
|
||||
|
||||
// The import directory is the second directory (index 1), as can be seen in winnt.h:
|
||||
// #define IMAGE_DIRECTORY_ENTRY_IMPORT 1
|
||||
const importDirectoryRva = dllFileView.getUint32(startOfDataDirectories + 0x08, true);
|
||||
const importDirectorySize = dllFileView.getUint32(startOfDataDirectories + 0x0C, true);
|
||||
|
||||
// No imports, also not our problem.
|
||||
if (importDirectorySize < SIZEOF_IMAGE_IMPORT_DESCRIPTOR) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const importDirectoryOffset = rva2offset(importDirectoryRva);
|
||||
|
||||
if (importDirectoryOffset === -1) {
|
||||
return "Could not convert import directory RVA to offset.";
|
||||
}
|
||||
|
||||
const importDllNameRva = dllFileView.getUint32(importDirectoryOffset + 12, true);
|
||||
const importDllNameOffset = rva2offset(importDllNameRva);
|
||||
|
||||
if (importDllNameOffset === -1) {
|
||||
return "Could not convert import DLL name RVA to offset.";
|
||||
}
|
||||
|
||||
if (bytesMatch(dllFile, importDllNameOffset, CRACKPROOF_KERNEL32_DLL)) {
|
||||
return "This executable is packed with CrackProof. Please obtain an unpacked executable.";
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
class PatchContainer {
|
||||
constructor(patchers) {
|
||||
this.patchers = patchers;
|
||||
@@ -805,6 +916,14 @@ class PatchContainer {
|
||||
// clear logs
|
||||
self.errorDiv.textContent = '';
|
||||
self.successDiv.textContent = '';
|
||||
|
||||
const error = checkUnpackedExecutable(new Uint8Array(e.target.result));
|
||||
|
||||
if (error) {
|
||||
self.errorDiv.innerHTML = error;
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < self.patchers.length; i++) {
|
||||
// reset text and buttons
|
||||
self.forceLoadButtons[i].style.display = 'none';
|
||||
@@ -1044,4 +1163,4 @@ class Patcher {
|
||||
window.Patcher = Patcher;
|
||||
window.PatchContainer = PatchContainer;
|
||||
|
||||
})(window, document);
|
||||
})(window, document);
|
||||
|
||||
Reference in New Issue
Block a user