diff --git a/src/core/lib/FileSignatures.mjs b/src/core/lib/FileSignatures.mjs index dc7ced4d..17b17f23 100644 --- a/src/core/lib/FileSignatures.mjs +++ b/src/core/lib/FileSignatures.mjs @@ -1282,17 +1282,30 @@ export const FILE_SIGNATURES = { extension: "dylib", mime: "application/octet-stream", description: "", - signature: { - 0: 0xca, - 1: 0xfe, - 2: 0xba, - 3: 0xbe, - 4: 0x00, - 5: 0x00, - 6: 0x00, - 7: [0x01, 0x02, 0x03] - }, - extractor: null + signature: [ + { + 0: 0xca, + 1: 0xfe, + 2: 0xba, + 3: 0xbe, + 4: 0x00, + 5: 0x00, + 6: 0x00, + 7: [0x01, 0x02, 0x03] + }, + { + 0: 0xce, + 1: 0xfa, + 2: 0xed, + 3: 0xfe, + 4: 0x07, + 5: 0x00, + 6: 0x00, + 7: 0x00, + 8: [0x01, 0x02, 0x03] + } + ], + extractor: extractMACHO }, { name: "MacOS Mach-O 64-bit object", @@ -1305,7 +1318,7 @@ export const FILE_SIGNATURES = { 2: 0xed, 3: 0xfe }, - extractor: null + extractor: extractMACHO }, { name: "Adobe Flash", @@ -1404,7 +1417,7 @@ export const FILE_SIGNATURES = { 260: 0x61, 261: 0x72 }, - extractor: null + extractor: extractTAR }, { name: "Roshal Archive", @@ -2720,6 +2733,154 @@ export function extractZIP(bytes, offset) { } +/** + * MACHO extractor + * + * @param {Uint8Array} bytes + * @param {number} offset + * @returns {Uint8Array} + */ +export function extractMACHO(bytes, offset) { + + // Magic bytes. + const MHCIGAM64 = "207250237254"; + const MHMAGIC64 = "254237250207"; + const MHCIGAM = "206250237254"; + + + /** + * Checks to see if the file is 64-bit. + * + * @param {string} magic + * @returns {bool} + */ + function isMagic64(magic) { + return magic === MHCIGAM64 || magic === MHMAGIC64; + } + + + /** + * Checks the endianness of the file. + * + * @param {string} magic + * @returns {bool} + */ + function shouldSwapBytes(magic) { + return magic === MHCIGAM || magic === MHCIGAM64; + } + + + /** + * Jumps through segment information and calculates the sum of the segement sizes. + * + * @param {Stream} stream + * @param {number} offset + * @param {string} isSwap + * @param {number} ncmds + * @returns {number} + */ + function dumpSegmentCommands(stream, offset, isSwap, ncmds) { + let total = 0; + const LCSEGEMENT64 = 0x19; + const LCSEGEMENT = 0x1; + + for (let i = 0; i < ncmds; i++) { + + // Move to start of segment. + stream.moveTo(offset); + const cmd = stream.readInt(4, isSwap); + if (cmd === LCSEGEMENT64) { + + // Move to size of segment field. + stream.moveTo(offset + 48); + + // Extract size of segement. + total += stream.readInt(8, isSwap); + stream.moveTo(offset + 4); + + // Move to offset of next segment. + offset += stream.readInt(4, isSwap); + } else if (cmd === LCSEGEMENT) { + stream.moveTo(offset + 36); + + // Extract size of segement. + total += stream.readInt(4, isSwap); + stream.moveTo(offset + 4); + offset += stream.readInt(4, isSwap); + } + } + return total; + } + + + /** + * Reads the number of command segments. + * + * @param {Stream} stream + * @param {bool} is64 + * @param {string} isSwap + * @returns {number} + */ + function dumpMachHeader(stream, is64, isSwap) { + let loadCommandsOffset = 28; + if (is64) + loadCommandsOffset += 4; + + // Move to number of commands field. + stream.moveTo(16); + const ncmds = stream.readInt(4, isSwap); + return dumpSegmentCommands(stream, loadCommandsOffset, isSwap, ncmds); + } + + + const stream = new Stream(bytes.slice(offset)); + const magic = stream.getBytes(4).join(""); + + // Move to the end of the final segment. + stream.moveTo(dumpMachHeader(stream, isMagic64(magic), shouldSwapBytes(magic) ? "le" : "be")); + return stream.carve(); +} + + +/** + * TAR extractor. + * + * @param {Uint8Array} bytes + * @param {number} offset + * @returns {Uint8Array} + */ +export function extractTAR(bytes, offset) { + const stream = new Stream(bytes.slice(offset)); + while (stream.hasMore()) { + + // Move to ustar identifier. + stream.moveForwardsBy(0x101); + if (stream.getBytes(5).join("") !== [0x75, 0x73, 0x74, 0x61, 0x72].join("")) { + // Reverse back to the end of the last section. + stream.moveBackwardsBy(0x106); + break; + } + + // Move back to file size field. + stream.moveBackwardsBy(0x8a); + let fsize = 0; + + // Read file size field. + stream.getBytes(11).forEach((element, index) => { + fsize += (element - 48).toString(); + }); + + // Round number up from octet to nearest 512. + fsize = (Math.ceil(parseInt(fsize, 8) / 512) * 512); + + // Move forwards to the end of that file. + stream.moveForwardsBy(fsize + 0x179); + } + stream.consumeWhile(0x00); + return stream.carve(); +} + + /** * PNG extractor. *