Skip to content

Instantly share code, notes, and snippets.

@AkshayJainG
Forked from ChiChou/macho.js
Created April 25, 2021 15:56
Show Gist options
  • Save AkshayJainG/9725b3b56025c97cbdecea01c6c334fc to your computer and use it in GitHub Desktop.
Save AkshayJainG/9725b3b56025c97cbdecea01c6c334fc to your computer and use it in GitHub Desktop.
Frida in-memory Mach-O parser
// 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