-
-
Save AkshayJainG/9725b3b56025c97cbdecea01c6c334fc to your computer and use it in GitHub Desktop.
Frida in-memory Mach-O parser
This file contains 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