Last active
February 26, 2025 06:59
-
-
Save ChiChou/cc9b95a7c42bd0988a453716f18a2aaf to your computer and use it in GitHub Desktop.
Frida in-memory Mach-O parser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// to speed up, I removed all data validation | |
function MemoryBuffer(address, size) { | |
this.base = address | |
if (!size) { | |
// const range = Process.findRangeByAddress(address) | |
// if (!range) | |
// throw new Error('invalid address: ' + address) | |
// this.length = range.base.add(range.size).sub(address).toInt32() | |
this.length = 4096 | |
} else { | |
this.length = size | |
} | |
} | |
const mapping = [ | |
['Int', 'Int', 4], | |
['UInt', 'UInt', 4], | |
['Float', 'Float', 4], | |
['Double', 'Double', 8], | |
['Int8', 'S8', 1], | |
['UInt8', 'U8', 1], | |
['Int16', 'S16', 2], | |
['UInt16', 'U16', 2], | |
['Int32', 'S32', 4], | |
['UInt32', 'U32', 4] | |
] | |
const isLE = ((new Uint32Array((new Uint8Array([1, 2, 3, 4])).buffer))[0] === 0x04030201) | |
const proto = MemoryBuffer.prototype | |
proto.slice = function(begin, end) { | |
// if (isNaN(begin) || begin < 0) { | |
// throw new Error('invalid offset: ' + begin) | |
// } | |
size = (typeof end === 'undefined' ? this.length : end ) - begin | |
/* if (isNaN(end) || end > this.length) { | |
throw new Error('invalid end: ' + end) | |
} */ | |
return new MemoryBuffer(this.base.add(begin), size) | |
} | |
proto.toString = function() { | |
try { | |
return Memory.readUtf8String(this.base) | |
} catch(e) { | |
return '(invalid utf8)' | |
} | |
} | |
const noImpl = function() { | |
throw new Error('not implemented') | |
} | |
mapping.forEach(function(type) { | |
const method = type[0] | |
const dest = type[1] | |
const size = type[2] | |
proto['read' + method] = function(offset) { | |
// validate? | |
const address = this.base.add(offset) | |
return Memory['read' + dest](address) | |
} | |
// proto['write' + method] = function(offset, val) { | |
// const address = this.base.add(offset) | |
// return Memory['write' + dest](address) | |
// } | |
const inverse = function(offset) { | |
const address = this.base.add(offset) | |
const buf = new Buffer(Memory.readByteArray(address, size)) | |
return buf['read' + method + (isLE ? 'BE' : 'LE')]() | |
} | |
if (size > 1) { | |
// le, be | |
proto['read' + method + 'LE'] = isLE ? proto['read' + method] : inverse | |
proto['read' + method + 'BE'] = isLE ? inverse : proto['read' + method] | |
// readonly | |
proto['write' + method + 'LE'] = proto['write' + method + 'BE'] = noImpl | |
} | |
}) | |
// usage | |
const macho = require('macho') | |
const fatmacho = require('fatmacho') | |
const main = Process.enumerateModulesSync()[0] | |
const buffer = new MemoryBuffer(main.base, main.size) | |
var info = null | |
try { | |
const bins = fatmacho.parse(buffer) | |
console.log(bins) | |
info = macho.parse(bins[0].data) | |
} catch (ex) { | |
info = macho.parse(buffer) | |
} | |
const CSSLOT_CODEDIRECTORY = 0 | |
const CSSLOT_REQUIREMENTS = 2 | |
const CSSLOT_ENTITLEMENTS = 5 | |
function parseEntitlements(data) { | |
const count = data.readUInt32BE(8) | |
for (var i = 0; i < count; i++) { | |
const base = 8 * i | |
const type = data.readUInt32BE(base + 12) | |
const blob = data.readUInt32BE(base + 16) | |
if (type === CSSLOT_ENTITLEMENTS) { | |
const size = data.readUInt32BE(blob + 4) | |
const buf = data.slice(blob + 8, blob + size) | |
return Memory.readUtf8String(buf.base, buf.length) | |
} | |
} | |
return null; | |
} | |
info.cmds.forEach(function(cmd) { | |
if (cmd.type === 'code_signature') { | |
const result = parseEntitlements(buffer.slice(cmd.dataoff)) | |
console.log(result) | |
} | |
}) | |
// console.log(JSON.stringify(info, null, 4)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
And the sample output