UNPKG

bagbak

Version:

Dump iOS app from a jailbroken device, based on frida.re

120 lines (100 loc) 2.73 kB
import { open, stat } from 'fs/promises'; /** * @typedef MachO * * @property {string} path * @property {number} type * @property {EncryptInfo} encryptInfo * @property {number} encCmdOffset */ /** * @typedef EncryptInfo * * @property {number} offset * @property {number} size * @property {number} id */ export class NotMachOError extends Error { /** * * @param {import('fs').PathLike} path */ constructor(path) { super(`${path} is not a 64bit mach-o file`); this.name = 'NotMachOError'; } } export class InvalidMachOError extends Error { constructor(message) { super(message); this.name = 'InvalidMachOError'; } } export const MH_EXECUTE = 0x2; export const MH_DYLIB = 0x6; export const MH_DYLINKER = 0x7; export const MH_BUNDLE = 0x8; const LC_ENCRYPTION_INFO_64 = 0x2c; /** * * @param {PathLike} file * @return {Promise<MachO>} */ export async function parse(file) { const MH_MAGIC_64 = 0xfeedfacf; const HEADER_SIZE_64 = 8 * 4; const stats = await stat(file); if (!stats.isFile()) { throw new Error(`${file} is not a file`); } else if (stats.size < HEADER_SIZE_64) { throw new NotMachOError(file); } const fd = await open(file, 'r'); try { // nodejs doesn't have builtin mmap // use read instead const header = Buffer.alloc(HEADER_SIZE_64); await fd.read(header, 0, HEADER_SIZE_64, 0); const magic = header.readUInt32LE(0); if (MH_MAGIC_64 !== magic) { throw new NotMachOError(file); } const fileType = header.readUInt32LE(12); const ncmds = header.readUInt32LE(16); const sizeOfCmds = header.readUInt32LE(20); if (sizeOfCmds + HEADER_SIZE_64 > stats.size) { throw new InvalidMachOError('malformed mach-o'); } const buffer = Buffer.alloc(sizeOfCmds); await fd.read(buffer, 0, sizeOfCmds, HEADER_SIZE_64); const result = { path: file, type: fileType, encryptInfo: { offset: NaN, size: NaN, id: 0, }, encCmdOffset: NaN } for (let offset = 0, i = 0; offset + 8 < buffer.length, i < ncmds; i++) { const cmd = buffer.readUInt32LE(offset); const cmdsize = buffer.readUInt32LE(offset + 4); if (cmd === LC_ENCRYPTION_INFO_64) { const cryptoff = buffer.readUInt32LE(offset + 4 * 2); const cryptsize = buffer.readUInt32LE(offset + 4 * 3); const cryptid = buffer.readUInt32LE(offset + 4 * 4); result.encryptInfo = { offset: cryptoff, size: cryptsize, id: cryptid, }; result.encCmdOffset = offset + HEADER_SIZE_64; } offset += cmdsize; } return result; } finally { fd.close(); } }