def parseRequirements(self, signature, offset): prev = self._f.tell() true_offset = signature.getOffset() + offset self._f.seek(true_offset) magic = getInt(self._f) if magic != dictionary.signatures['REQUIREMENTS']: data = { 'offset': true_offset, 'magic': hex(magic), 'expected': hex(dictionary.signatures['REQUIREMENTS']) } a = Abnormality(title='BAD MAGIC - REQUIREMENTS', data=data) self.addAbnormality(a) self._f.seek(prev) return # Skip size self._f.read(4) count = getInt(self._f) while count > 0: req_type = dictionary.requirements[getInt(self._f)] offset = getInt(self._f) requirement = Requirement(req_type=req_type, offset=offset) self.parseRequirement(requirement, true_offset) signature.addRequirement(requirement) count -= 1 self._f.seek(prev)
def parseCerts(self, signature, offset): prev = self._f.tell() true_offset = signature.getOffset() + offset self._f.seek(true_offset) magic = getInt(self._f) if magic != dictionary.signatures['BLOBWRAPPER']: data = { 'offset': true_offset, 'magic': hex(magic), 'expected': hex(dictionary.signatures['BLOBWRAPPER']) } a = Abnormality(title='BAD MAGIC - BLOBWRAPPER', data=data) self.addAbnormality(a) self._f.seek(prev) return size = getInt(self._f) - 8 # out = open('cms', 'wb') # out.write(self._f.read(size)) # out.close() # exit(0) if size > 0: signed_data = cms.CMS(self._f.read(size), format='DER') for cert in signed_data.certs: serial = cert.serial subject = { 'country': self.getCertNameData(cert.subject, oid.Oid('C')), 'org': self.getCertNameData(cert.subject, oid.Oid('O')), 'org_unit': self.getCertNameData(cert.subject, oid.Oid('OU')), 'common_name': self.getCertNameData(cert.subject, oid.Oid('CN')) } issuer = { 'country': self.getCertNameData(cert.issuer, oid.Oid('C')), 'org': self.getCertNameData(cert.issuer, oid.Oid('O')), 'org_unit': self.getCertNameData(cert.issuer, oid.Oid('OU')), 'common_name': self.getCertNameData(cert.issuer, oid.Oid('CN')) } ca = cert.check_ca() cert = Certificate(serial=serial, subject=subject, issuer=issuer, ca=ca) signature.addCert(cert) else: data = { 'offset': true_offset, 'size': size } a = Abnormality(title='NON-POSITIVE CMS SIZE', data=data) self.addAbnormality(a) self._f.seek(prev)
def parseSig(self, macho): if not macho.hasLC('CODE_SIGNATURE'): return prev = self._f.tell() true_offset = (macho.getOffset() + macho.getLC('CODE_SIGNATURE').getData()['offset']) if true_offset >= self._file.getSize(): data = { 'offset': true_offset, 'file_size': self._file.getSize() } a = Abnormality(title='CODE_SIGNATURE OUT OF BOUNDS', data=data) self.addAbnormality(a) return self._f.seek(true_offset) magic = getInt(self._f) if magic != dictionary.signatures['EMBEDDED_SIGNATURE']: data = { 'offset': true_offset, 'magic': hex(magic), 'expected': hex(dictionary.signatures['EMBEDDED_SIGNATURE']) } a = Abnormality(title='BAD MAGIC - EMBEDDED_SIGNATURE', data=data) self.addAbnormality(a) self._f.seek(prev) return size = getInt(self._f) count = getInt(self._f) signature = Signature(offset=true_offset, size=size, count=count) while count > 0: index_type = getInt(self._f) try: index_type = dictionary.indeces[index_type] except: data = { 'offset': self._f.tell() - 4, 'index_type': index_type } a = Abnormality(title='INVALID CODE_SIGNATURE INDEX_TYPE', data=data) self.addAbnormality(a) offset = getInt(self._f) if index_type == 'SignatureSlot': self.parseCerts(signature, offset) elif index_type == 'CodeDirectorySlot': self.parseCodeDirectory(signature, offset) elif index_type == 'EntitlementSlot': self.parseEntitlement(signature, offset) elif index_type == 'RequirementsSlot': self.parseRequirements(signature, offset) count -= 1 macho.setSignature(signature) self._f.seek(prev)
def parseTwoLevelHints(self, lc): offset = getInt(self._f) nhints = getInt(self._f) if self._macho.isLittle(): offset = little(offset, 'I') nhints = little(nhints, 'I') lc.addData('offset', offset) lc.addData('nhints', nhints) self._macho.addLC(lc)
def parseLinkedITData(self, lc): offset = getInt(self._f) size = getInt(self._f) if self._macho.isLittle(): offset = little(offset, 'I') size = little(size, 'I') lc.addData('offset', offset) lc.addData('size', size) self._macho.addLC(lc)
def identifyFile(self, offset): prev = self._f.tell() self._f.seek(offset) magic = getInt(self._f) self._f.seek(prev) if magic not in dictionary.machos: return magic return dictionary.machos[magic]
def parseSection(self): name = strip(self._f.read(16)) segname = strip(self._f.read(16)) addr = getInt(self._f) if self._macho.is32Bit() else getLL(self._f) size = getInt(self._f) if self._macho.is32Bit() else getLL(self._f) offset = getInt(self._f) align = getInt(self._f) reloff = getInt(self._f) nreloc = getInt(self._f) flags = getInt(self._f) self._f.read(8) if self._macho.is32Bit() else self._f.read(12) if self._macho.isLittle(): addr = little(addr, 'I') if self._macho.is32Bit() \ else little(addr, 'Q') size = little(size, 'I') if self._macho.is32Bit() \ else little(size, 'Q') offset = little(offset, 'I') align = little(align, 'I') reloff = little(reloff, 'I') nreloc = little(nreloc, 'I') flags = little(flags, 'I') section = Section(name=name, segname=segname, addr=addr, offset=offset, align=align, reloff=reloff, nreloc=nreloc, size=size) self.parseSectionFlags(section, flags) return section
def parsePrebindCksum(self, lc): cksum = getInt(self._f) if self._macho.isLittle(): cksum = little(cksum, 'I') lc.addData('cksum', cksum) self._macho.addLC(lc)
def parseEncryptionInfo(self, lc): offset = getInt(self._f) size = getInt(self._f) id = getInt(self._f) if self._macho.isLittle(): offset = little(offset, 'I') size = little(size, 'I') id = little(id, 'I') lc.addData('offset', offset) lc.addData('size', size) lc.addData('id', id) if lc.getCmd() == 'ENCRYPTION_INFO_64': # Skip padding self._f.read(4) self._macho.addLC(lc)
def parseSegment(self, lc): name = strip(self._f.read(16)) vmaddr = getInt(self._f) if self._macho.is32Bit() else getLL(self._f) vmsize = getInt(self._f) if self._macho.is32Bit() else getLL(self._f) offset = getInt(self._f) if self._macho.is32Bit() else getLL(self._f) segsize = getInt(self._f) if self._macho.is32Bit() else getLL(self._f) maxprot = getInt(self._f) initprot = getInt(self._f) nsects = getInt(self._f) flags = getInt(self._f) if self._macho.isLittle(): vmaddr = little(vmaddr, 'I') if self._macho.is32Bit() \ else little(vmaddr, 'Q') vmsize = little(vmsize, 'I') if self._macho.is32Bit() \ else little(vmsize, 'Q') offset = little(offset, 'I') if self._macho.is32Bit() \ else little(offset, 'Q') segsize = little(segsize, 'I') if self._macho.is32Bit() \ else little(segsize, 'Q') maxprot = little(maxprot, 'I') initprot = little(initprot, 'I') nsects = little(nsects, 'I') flags = little(flags, 'I') maxprot = dictionary.protections[maxprot & 0b111] initprot = dictionary.protections[initprot & 0b111] segment = Segment(cmd=lc.getCmd(), size=lc.getSize(), name=name, vmaddr=vmaddr, vmsize=vmsize, offset=offset, segsize=segsize, maxprot=maxprot, initprot=initprot, nsects=nsects) if self._macho.is32Bit(): sect_size = 68 else: sect_size = 80 for i in range(segment.getNSects()): if self._f.tell() + sect_size > self._file_size: data = {'offset': self._f.tell(), 'file_size': self._file_size} a = Abnormality(title='SECTION OUT OF BOUNDS', data=data) break sect = self.parseSection() segment.addSect(sect) self.parseSegmentFlags(segment, flags) self._macho.addLC(segment)
def parseEntitlement(self, signature, offset): prev = self._f.tell() true_offset = signature.getOffset() + offset self._f.seek(true_offset) magic = getInt(self._f) if magic != dictionary.signatures['ENTITLEMENT']: data = { 'offset': true_offset, 'magic': hex(magic), 'expected': hex(dictionary.signatures['ENTITLEMENT']) } a = Abnormality(title='BAD MAGIC - ENTITLEMENT', data=data) self.addAbnormality(a) self._f.seek(prev) return size = getInt(self._f) - 8 plist = plistlib.readPlistFromString(self._f.read(size)) entitlement = Entitlement(size=size, plist=plist) signature.addEntitlement(entitlement) self._f.seek(prev)
def parseThread(self, lc): state = getInt(self._f) count = getInt(self._f) self._f.read(lc.getSize() - 16) if self._macho.isLittle(): state = little(state, 'I') count = little(count, 'I') try: state = dictionary.thread_states[state] except: data = {'offset': self._f.tell() - lc.getSize(), 'state': state} a = Abnormality(title='INVALID THREAD STATE FLAVOR', data=data) self.addAbnormality(a) lc.addData('state', state) lc.addData('count', count) self._macho.addLC(lc)
def parseRoutines(self, lc): if lc.getCmd() == 'ROUTINES': init_address = getInt(self._f) init_module = getInt(self._f) if self._macho.isLittle(): init_address = little(init_address, 'I') init_module = little(init_module, 'I') self._f.read(24) else: init_address = getLL(self._f) init_module = getLL(self._f) if self._macho.isLittle(): init_address = little(init_address, 'Q') init_module = little(init_module, 'Q') self._f.read(48) lc.addData('init_address', init_address) lc.addData('init_module', init_module) self._macho.addLC(lc)
def parseUniversal(self): self._f.seek(0) # skip magic self._f.read(4) nmachos = getInt(self._f) u = Universal(nmachos=nmachos) u_size = self.getFile().getSize() for i in range(u.getNMachOs()): # skip cputype, subtype self._f.read(8) offset = getInt(self._f) size = getInt(self._f) # Abnormality OUT_OF_BOUNDS check if offset + size > u_size: data = { 'offset': offset, 'size': size, 'file_size': u_size } a = Abnormality(title='MACH-O OUT OF BOUNDS', data=data) self.addAbnormality(a) continue # skip align self._f.read(4) identity = self.identifyFile(offset) # Abnormality BAD_MAGIC check if identity not in dictionary.machos.values(): data = { 'offset': offset, 'magic': identity, } a = Abnormality(title='BAD MAGIC - MACH-O') self.addAbnormality(a) continue u.addMachO(MachO(archive=True, offset=offset, arch=identity[0], endi=identity[1], size=size)) for i in u.genMachOs(): self.parseMachO(i) self._file.setContent(u)
def parseDySymTab(self, lc): il = getInt(self._f) nl = getInt(self._f) ie = getInt(self._f) ne = getInt(self._f) iu = getInt(self._f) nu = getInt(self._f) self._f.read(lc.getSize() - 32) if self._macho.isLittle(): il = little(il, 'I') nl = little(nl, 'I') ie = little(ie, 'I') ne = little(ne, 'I') iu = little(iu, 'I') nu = little(nu, 'I') self._macho.getSymTab().setIL(il) self._macho.getSymTab().setNL(nl) self._macho.getSymTab().setIE(ie) self._macho.getSymTab().setNE(ne) self._macho.getSymTab().setIU(iu) self._macho.getSymTab().setNU(nu) lc.addData('il', il) lc.addData('nl', nl) lc.addData('ie', ie) lc.addData('ne', ne) lc.addData('iu', iu) lc.addData('nu', nu) self._macho.addLC(lc)
def parseSegment(self, lc): name = strip(self._f.read(16)) vmaddr = getInt(self._f) if self._macho.is32Bit() else getLL(self._f) vmsize = getInt(self._f) if self._macho.is32Bit() else getLL(self._f) offset = getInt(self._f) if self._macho.is32Bit() else getLL(self._f) segsize = getInt(self._f) if self._macho.is32Bit() else getLL(self._f) maxprot = getInt(self._f) initprot = getInt(self._f) nsects = getInt(self._f) flags = getInt(self._f) if self._macho.isLittle(): vmaddr = little(vmaddr, 'I') if self._macho.is32Bit() \ else little(vmaddr, 'Q') vmsize = little(vmsize, 'I') if self._macho.is32Bit() \ else little(vmsize, 'Q') offset = little(offset, 'I') if self._macho.is32Bit() \ else little(offset, 'Q') segsize = little(segsize, 'I') if self._macho.is32Bit() \ else little(segsize, 'Q') maxprot = little(maxprot, 'I') initprot = little(initprot, 'I') nsects = little(nsects, 'I') flags = little(flags, 'I') maxprot = dictionary.protections[maxprot & 0b111] initprot = dictionary.protections[initprot & 0b111] segment = Segment(cmd=lc.getCmd(), size=lc.getSize(), name=name, vmaddr=vmaddr, vmsize=vmsize, offset=offset, segsize=segsize, maxprot=maxprot, initprot=initprot, nsects=nsects) if self._macho.is32Bit(): sect_size = 68 else: sect_size = 80 for i in range(segment.getNSects()): if self._f.tell() + sect_size > self._file_size: data = { 'offset': self._f.tell(), 'file_size': self._file_size } a = Abnormality(title='SECTION OUT OF BOUNDS', data=data) break sect = self.parseSection() segment.addSect(sect) self.parseSegmentFlags(segment, flags) self._macho.addLC(segment)
def parseThread(self, lc): state = getInt(self._f) count = getInt(self._f) self._f.read(lc.getSize() - 16) if self._macho.isLittle(): state = little(state, 'I') count = little(count, 'I') try: state = dictionary.thread_states[state] except: data = { 'offset': self._f.tell() - lc.getSize(), 'state': state } a = Abnormality(title='INVALID THREAD STATE FLAVOR', data=data) self.addAbnormality(a) lc.addData('state', state) lc.addData('count', count) self._macho.addLC(lc)
def parseLoadDylib(self, lc): offset = getInt(self._f) if self._macho.isLittle(): offset = little(offset, 'I') # skip to dylib self._f.read(offset - 12) dylib = strip(self._f.read(lc.getSize() - 24)) self._macho.addDylib(dylib) lc.addData('dylib', dylib) self._macho.addLC(lc)
def parseVersionMinOS(self, lc): version = getInt(self._f) sdk = getInt(self._f) if self._macho.isLittle(): version = little(version, 'I') sdk = little(sdk, 'I') vx = version >> 16 vy = (version >> 8) & 0xff vz = version & 0xff version = OSVersion(vx=vx, vy=vy, vz=vz) sx = str(sdk >> 16) sy = str((sdk >> 8) & 0xff) sz = str(sdk & 0xff) sdk = sx + '.' + sy + '.' + sz lc.addData('version', version.getVersion()) lc.addData('sdk', sdk) self._macho.setMinOS(version) self._macho.addLC(lc)
def parseSymTab(self, lc): symoff = getInt(self._f) nsyms = getInt(self._f) stroff = getInt(self._f) strsize = getInt(self._f) if self._macho.isLittle(): symoff = little(symoff, 'I') nsyms = little(nsyms, 'I') stroff = little(stroff, 'I') strsize = little(strsize, 'I') symtab = SymbolTable(offset=symoff, nsyms=nsyms) strtab = StringTable(offset=stroff, size=strsize) self._macho.setSymTab(symtab) self._macho.setStrTab(strtab) lc.addData('symoff', symoff) lc.addData('nsyms', nsyms) lc.addData('stroff', stroff) lc.addData('strsize', strsize) self._macho.addLC(lc)
def parseDyldInfo(self, lc): rebase_off = getInt(self._f) rebase_size = getInt(self._f) bind_off = getInt(self._f) bind_size = getInt(self._f) weak_bind_off = getInt(self._f) weak_bind_size = getInt(self._f) lazy_bind_off = getInt(self._f) lazy_bind_size = getInt(self._f) export_off = getInt(self._f) export_size = getInt(self._f) if self._macho.isLittle(): rebase_off = little(rebase_off, 'I') rebase_size = little(rebase_size, 'I') bind_off = little(bind_off, 'I') bind_size = little(bind_size, 'I') weak_bind_off = little(weak_bind_off, 'I') weak_bind_size = little(weak_bind_size, 'I') lazy_bind_off = little(lazy_bind_off, 'I') lazy_bind_size = little(lazy_bind_size, 'I') export_off = little(export_off, 'I') export_size = little(export_size, 'I') lc.addData('rebase_off', rebase_off) lc.addData('rebase_size', rebase_size) lc.addData('bind_off', bind_off) lc.addData('bind_size', bind_size) lc.addData('weak_bind_off', weak_bind_off) lc.addData('weak_bind_size', weak_bind_size) lc.addData('lazy_bind_off', lazy_bind_off) lc.addData('lazy_bind_size', lazy_bind_size) lc.addData('export_off', export_off) lc.addData('export_size', export_size) self._macho.addLC(lc)
def parseLinkerOption(self, lc): count = getInt(self._f) if self._macho.isLittle(): count = little(count, 'I') linker_options = [] start = self._f.tell() for i in range(count): linker_option = readstring(self._f) linker_options.append(linker_option) length = self._f.tell() - start self._f.read(lc.getSize() - length - 12) lc.addData('count', count) lc.addData('linker_options', linker_options) self._macho.addLC(lc)
def parseRequirement(self, requirement, offset): prev = self._f.tell() true_offset = offset + requirement.getOffset() self._f.seek(true_offset) magic = getInt(self._f) if magic != dictionary.signatures['REQUIREMENT']: data = { 'offset': true_offset, 'magic': hex(magic), 'expected': hex(dictionary.signatures['REQUIREMENT']) } a = Abnormality(title='BAD MAGIC - REQUIREMENT', data=data) self.addAbnormality(a) self._f.seek(prev) return # Skip size and kind self._f.read(8) requirement.setExpression(self.parseExpression(False)) self._f.seek(prev)
def parseMatch(self): match_type = getInt(self._f) if match_type in dictionary.matches: match_type = dictionary.matches[match_type] if match_type == 'matchExists': return ' /* exists */' elif match_type == 'matchEqual': return ' = "' + str(self.parseData()) + '"' elif match_type == 'matchContains': return ' ~ "' + str(self.parseData()) + '"' elif match_type == 'matchBeginsWith': return ' = "' + str(self.parseData()) + '*"' elif match_type == 'matchEndsWith': return ' = "*' + str(self.parseData()) + '"' elif match_type == 'matchLessThan': return ' < ' + str(int(self.parseData().encode('hex'), 16)) elif match_type == 'matchGreaterThan': return ' > ' + str(int(self.parseData().encode('hex'), 16)) elif match_type == 'matchLessEqual': return ' <= ' + str(int(self.parseData().encode('hex'), 16)) elif match_type == 'matchGreaterEqual': return ' >= ' + str(int(self.parseData().encode('hex'), 16)) else: return ' UNKNOWN MATCH TYPE (' + str(match_type) + ')'
def parseExpression(self, in_or): # Zero out flags in high byte operator = dictionary.operators[getInt(self._f) & 0xfff] expression = '' if operator == 'False': expression += 'never' elif operator == 'True': expression += 'always' elif operator == 'Ident': expression += 'identity "' + str(self.parseData()) + '"' elif operator == 'AppleAnchor': expression += 'anchor apple' elif operator == 'AppleGenericAnchor': expression += 'anchor apple generic' elif operator == 'AnchorHash': cert_slot = getInt(self._f) if cert_slot in dictionary.cert_slots: cert_slot = dictionary.cert_slots[cert_slot] else: cert_slot = str(cert_slot) expression += ('certificate ' + cert_slot + ' = ' + str(self.parseData().encode('hex'))) elif operator == 'InfoKeyValue': expression += ('info[' + str(self.parseData()) + '] = "' + str(self.parseData()) + '"') elif operator == 'And': if in_or: expression += ('(' + self.parseExpression(False) + ' and ' + self.parseExpression(False) + ')') else: expression += (self.parseExpression(False) + ' and ' + self.parseExpression(False)) elif operator == 'Or': if in_or: expression += ('(' + self.parseExpression(True) + ' or ' + self.parseExpression(True) + ')') else: expression += (self.parseExpression(True) + ' or ' + self.parseExpression(True)) elif operator == 'Not': expression += '! ' + self.parseExpression(False) elif operator == 'CDHash': expression += 'cdhash ' + str(self.parseData().encode('hex')) elif operator == 'InfoKeyField': expression += ('info[' + str(self.parseData()) + ']' + self.parseMatch()) elif operator == 'EntitlementField': expression += ('entitlement[' + str(self.parseData()) + ']' + self.parseMatch()) elif operator == 'CertField': cert_slot = getInt(self._f) if cert_slot in dictionary.cert_slots: cert_slot = dictionary.cert_slots[cert_slot] else: cert_slot = str(cert_slot) expression += ('certificate ' + cert_slot + '[' + str(self.parseData()) + ']' + self.parseMatch()) elif operator == 'CertGeneric': cert_slot = getInt(self._f) if cert_slot in dictionary.cert_slots: cert_slot = dictionary.cert_slots[cert_slot] else: cert_slot = str(cert_slot) length = getInt(self._f) expression += ('certificate ' + cert_slot + '[field.' + self.toOID(length) + ']' + self.parseMatch()) elif operator == 'CertPolicy': cert_slot = getInt(self._f) if cert_slot in dictionary.cert_slots: cert_slot = dictionary.cert_slots[cert_slot] else: cert_slot = str(cert_slot) expression += ('certificate ' + cert_slot + '[policy.' + str(self.parseData()) + ']' + self.parseMatch()) elif operator == 'TrustedCert': cert_slot = getInt(self._f) if cert_slot in dictionary.cert_slots: cert_slot = dictionary.cert_slots[cert_slot] else: cert_slot = str(cert_slot) expression += 'certificate ' + cert_slot + ' trusted' elif operator == 'TrustedCerts': expression += 'anchor trusted' elif operator == 'NamedAnchor': expression += 'anchor apple ' + str(self.parseData()) elif operator == 'NamedCode': expression += '(' + str(self.parseData()) + ')' elif operator == 'Platform': expression += 'platform = ' + str(getInt(self._f)) if isinstance(expression, unicode): return expression else: return unicode(expression, errors='replace')
def parseLCs(self): for i in range(self._macho.getNLCs()): cmd = getInt(self._f) size = getInt(self._f) if self._macho.getEndi() == 'little': cmd = little(cmd, 'I') size = little(size, 'I') # print ('(offset, cmd, size): (' + str(self._f.tell() - 8) + ', ' + # str(cmd) + ', ' + str(size) + ')') try: cmd = dictionary.loadcommands[cmd] except: data = {'offset': self._f.tell() - 8, 'cmd': cmd} a = Abnormality(title='UNKNOWN LOADCOMMAND', data=data) self.addAbnormality(a) lc = LoadCommand(cmd=cmd, size=size) self._macho.addLC(lc) self._f.read(size - 8) continue lc = LoadCommand(cmd=cmd, size=size) if cmd == 'SEGMENT' or cmd == 'SEGMENT_64': self.parseSegment(lc) elif cmd == 'SYMTAB': self.parseSymTab(lc) elif cmd == 'SYMSEG': self.parseSymSeg(lc) elif cmd == 'THREAD' or cmd == 'UNIXTHREAD': self.parseThread(lc) elif cmd == 'LOADFVMLIB' or cmd == 'IDFVMLIB': self.parseFVMLib(lc) elif cmd == 'IDENT': self.parseIdent(lc) elif cmd == 'FVMFILE': self.parseFVMFile(lc) elif cmd == 'PREPAGE': self.parsePrePage(lc) elif cmd == 'DYSYMTAB': self.parseDySymTab(lc) elif (cmd == 'LOAD_DYLIB' or cmd == 'ID_DYLIB' or cmd == 'LAZY_LOAD_DYLIB' or cmd == 'LOAD_WEAK_DYLIB' or cmd == 'REEXPORT_DYLIB' or cmd == 'LOAD_UPWARD_DYLIB'): self.parseLoadDylib(lc) elif (cmd == 'LOAD_DYLINKER' or cmd == 'ID_DYLINKER' or cmd == 'DYLD_ENVIRONMENT'): self.parseLoadDylinker(lc) elif cmd == 'PREBOUND_DYLIB': self.parsePreboundDylib(lc) elif cmd == 'ROUTINES' or cmd == 'ROUTINES_64': self.parseRoutines(lc) elif (cmd == 'SUB_FRAMEWORK' or cmd == 'SUB_UMBRELLA' or cmd == 'SUB_CLIENT' or cmd == 'SUB_LIBRARY'): self.parseSubStuff(lc) elif cmd == 'TWOLEVEL_HINTS': self.parseTwoLevelHints(lc) elif cmd == 'PREBIND_CKSUM': self.parsePrebindCksum(lc) elif cmd == 'UUID': self.parseUUID(lc) elif (cmd == 'CODE_SIGNATURE' or cmd == 'SEGMENT_SPLIT_INFO' or cmd == 'FUNCTION_STARTS' or cmd == 'DATA_IN_CODE' or cmd == 'DYLIB_CODE_SIGN_DRS' or cmd == 'LINKER_OPTIMIZATION_HINT'): self.parseLinkedITData(lc) elif cmd == 'ENCRYPTION_INFO' or cmd == 'ENCRYPTION_INFO_64': self.parseEncryptionInfo(lc) elif cmd == 'DYLD_INFO' or cmd == 'DYLD_INFO_ONLY': self.parseDyldInfo(lc) elif (cmd == 'VERSION_MIN_MACOSX' or cmd == 'VERSION_MIN_IPHONEOS' or cmd == 'VERSION_MIN_WATCHOS'): self.parseVersionMinOS(lc) elif cmd == 'SOURCE_VERSION': self.parseSourceVersion(lc) elif cmd == 'LINKER_OPTION': self.parseLinkerOption(lc) elif cmd == 'RPATH': self.parseRPath(lc) elif cmd == 'MAIN': self.parseMain(lc)
def parseData(self): length = getInt(self._f) data = self._f.read(length) # Skip padding self._f.read(-length & 3) return data
def parseMachO(self, macho): self._f.seek(macho.getOffset()) # skip magic self._f.read(4) cputype = getInt(self._f) # print 'cputype: ' + str(cputype) # print 'offset: ' + str(self._f.tell()) subtype = getInt(self._f) filetype = getInt(self._f) nlcs = getInt(self._f) slcs = getInt(self._f) flags = getInt(self._f) if macho.is64Bit(): # skip padding self._f.read(4) if macho.isLittle(): cputype = little(cputype, 'I') subtype = little(subtype, 'I') filetype = little(filetype, 'I') nlcs = little(nlcs, 'I') slcs = little(slcs, 'I') flags = little(flags, 'I') try: cpu = dictionary.cputypes[cputype][-2] except: cpu = cputype data = { 'offset': macho.getOffset() + 4, 'cputype': cputype } a = Abnormality(title='UNKNOWN CPUTYPE', data=data) self.addAbnormality(a) try: subtype = dictionary.cputypes[cputype][subtype] except: data = { 'offset': macho.getOffset() + 8, 'cputype': cputype, 'subtype': subtype } a = Abnormality(title='UNKNOWN SUBTYPE', data=data) self.addAbnormality(a) try: filetype = dictionary.filetypes[filetype] except: data = { 'offset': macho.getOffset() + 12, 'filetype': filetype } a = Abnormality(title='UNKNOWN FILETYPE', data=data) self.addAbnormality(a) flags = self.listMachOFlags(flags) macho.setCPUType(cpu) macho.setSubType(subtype) macho.setFileType(filetype) macho.setNLCs(nlcs) macho.setSLCs(slcs) macho.setFlags(flags) lc = LoadCommander(f=self._f, macho=macho, file_size=self._file.getSize()) lc.parseLCs() self._abnormalities += lc.getAbnormalities() # Need to investigate whether the presence of a # symbol/string table is expected and whether the # abscence is indicative of shenanigans. if macho.hasLC('SYMTAB'): self.parseSyms(macho) self.parseImportsAndStrings(macho) if macho.hasLC('CODE_SIGNATURE'): self.parseSig(macho) if not macho.isArchive(): self._file.setContent(macho)
def parseCodeDirectory(self, signature, offset): prev = self._f.tell() true_offset = signature.getOffset() + offset self._f.seek(true_offset) magic = getInt(self._f) if magic != dictionary.signatures['CODEDIRECTORY']: data = { 'offset': true_offset, 'magic': hex(magic), 'expected': hex(dictionary.signatures['CODEDIRECTORY']) } a = Abnormality(title='BAD MAGIC - CODEDIRECTORY', data=data) self.addAbnormality(a) self._f.seek(prev) return # Skip size self._f.read(4) version = getInt(self._f) # Not sure how to parse flags yet... flags = getInt(self._f) hash_offset = getInt(self._f) ident_offset = getInt(self._f) n_special_slots = getInt(self._f) n_code_slots = getInt(self._f) code_limit = getInt(self._f) hash_size = int(self._f.read(1).encode('hex'), 16) hash_type = dictionary.hashes[int(self._f.read(1).encode('hex'), 16)] if version >= 0x20200: platform = int(self._f.read(1).encode('hex'), 16) else: # Skip spare1 self._f.read(1) page_size = int(round(exp(int(self._f.read(1).encode('hex'), 16) * log(2)))) # Skip spare2 self._f.read(4) if version >= 0x20100: scatter_offset = getInt(self._f) if version >= 0x20200: team_id_offset = getInt(self._f) self._f.seek(true_offset + team_id_offset) team_id = readstring(self._f) self._f.seek(true_offset + ident_offset) identity = readstring(self._f) codedirectory = CodeDirectory(version=version, flags=flags, hash_offset=hash_offset, n_special_slots=n_special_slots, n_code_slots=n_code_slots, code_limit=code_limit, hash_size=hash_size, hash_type=hash_type, page_size=page_size, identity=identity) if version >= 0x20100: codedirectory.setScatterOffset(scatter_offset) if version >= 0x20200: codedirectory.setPlatform(platform) codedirectory.setTeamIDOffset(team_id_offset) codedirectory.setTeamID(team_id) self._f.seek(true_offset + hash_offset - n_special_slots * hash_size) count = n_special_slots + n_code_slots while count > 0: hash = self._f.read(hash_size).encode('hex') codedirectory.addHash(hash) count -= 1 signature.setCodeDirectory(codedirectory) self._f.seek(prev)
def parseSyms(self, macho): prev = self._f.tell() true_offset = macho.getOffset() + macho.getSymTab().getOffset() if macho.is64Bit(): symbol_size = 60 else: symbol_size = 56 # print 'to:', true_offset # print macho.getOffset(), macho.getSize() if (true_offset < macho.getOffset() + macho.getSize() and true_offset < self._file.getSize()): self._f.seek(true_offset) for i in range(macho.getSymTab().getNSyms()): # print self._f.tell() if ((self._f.tell() + symbol_size > macho.getOffset() + macho.getSize()) or (self._f.tell() + symbol_size > self._file.getSize())): data = { 'offset': self._f.tell(), 'mach-o_size': macho.getSize(), 'mach-o_offset': macho.getOffset(), 'file_size': self._file.getSize() } a = Abnormality(title='REMAINING SYMBOLS OUT OF BOUNDS', data=data) self.addAbnormality(a) self._f.seek(prev) return else: index = getInt(self._f) sym_type = int(self._f.read(1).encode('hex'), 16) sect = int(self._f.read(1).encode('hex'), 16) desc = int(self._f.read(2).encode('hex'), 16) value = None if macho.is64Bit(): if macho.isLittle(): value = little(getLL(self._f), 'Q') else: value = getLL(self._f) else: if macho.isLittle(): value = little(getInt(self._f), 'I') else: value = getInt(self._f) if macho.isLittle(): index = little(index, 'I') if sym_type >= 32: if sym_type in dictionary.stabs: stab = dictionary.stabs[sym_type] else: offset = self._f.tell() - symbol_size data = { 'offset': offset, 'index': index, 'sym_type': sym_type, 'sect': sect, 'desc': desc, 'value': value } a = Abnormality(title='UNKNOWN STAB', data=data) self.addAbnormality(a) continue sym = Symbol(index=index, stab=stab, sect=sect, value=value) macho.getSymTab().addSym(sym) else: pext = sym_type & 0x10 if sym_type & 0x0e in dictionary.n_types: n_type = dictionary.n_types[sym_type & 0x0e] else: offset = self._f.tell() - symbol_size data = { 'offset': offset, 'index': index, 'pext': pext, 'n_type': sym_type & 0x0e, 'sect': sect, 'desc': desc, 'value': value } a = Abnormality(title='UNKNOWN N_TYPE', data=data) self.addAbnormality(a) ext = sym_type & 0x01 if macho.isLittle(): dylib = desc & 0x0f ref = (desc >> 8) & 0xff else: dylib = (desc >> 8) & 0xff ref = desc & 0x0f sym = Symbol(index=index, pext=pext, sym_type=n_type, ext=ext, sect=sect, dylib=dylib, ref=ref, value=value) macho.getSymTab().addSym(sym) # print self._f.tell() # print sym.getIndex(), sym.getValue() else: data = { 'offset': true_offset, 'mach-o_size': macho.getSize(), 'mach-o_offset': macho.getOffset(), 'file_size': self._file.getSize() } a = Abnormality(title='SYMBOL TABLE OUT OF BOUNDS', data=data) self.addAbnormality(a) self._f.seek(prev)
def parseLCs(self): for i in range(self._macho.getNLCs()): cmd = getInt(self._f) size = getInt(self._f) if self._macho.getEndi() == 'little': cmd = little(cmd, 'I') size = little(size, 'I') # print ('(offset, cmd, size): (' + str(self._f.tell() - 8) + ', ' + # str(cmd) + ', ' + str(size) + ')') try: cmd = dictionary.loadcommands[cmd] except: data = { 'offset': self._f.tell() - 8, 'cmd': cmd } a = Abnormality(title='UNKNOWN LOADCOMMAND', data=data) self.addAbnormality(a) lc = LoadCommand(cmd=cmd, size=size) self._macho.addLC(lc) self._f.read(size - 8) continue lc = LoadCommand(cmd=cmd, size=size) if cmd == 'SEGMENT' or cmd == 'SEGMENT_64': self.parseSegment(lc) elif cmd == 'SYMTAB': self.parseSymTab(lc) elif cmd == 'SYMSEG': self.parseSymSeg(lc) elif cmd == 'THREAD' or cmd == 'UNIXTHREAD': self.parseThread(lc) elif cmd == 'LOADFVMLIB' or cmd == 'IDFVMLIB': self.parseFVMLib(lc) elif cmd == 'IDENT': self.parseIdent(lc) elif cmd == 'FVMFILE': self.parseFVMFile(lc) elif cmd == 'PREPAGE': self.parsePrePage(lc) elif cmd == 'DYSYMTAB': self.parseDySymTab(lc) elif (cmd == 'LOAD_DYLIB' or cmd == 'ID_DYLIB' or cmd == 'LAZY_LOAD_DYLIB' or cmd == 'LOAD_WEAK_DYLIB' or cmd == 'REEXPORT_DYLIB' or cmd == 'LOAD_UPWARD_DYLIB'): self.parseLoadDylib(lc) elif (cmd == 'LOAD_DYLINKER' or cmd == 'ID_DYLINKER' or cmd == 'DYLD_ENVIRONMENT'): self.parseLoadDylinker(lc) elif cmd == 'PREBOUND_DYLIB': self.parsePreboundDylib(lc) elif cmd == 'ROUTINES' or cmd == 'ROUTINES_64': self.parseRoutines(lc) elif (cmd == 'SUB_FRAMEWORK' or cmd == 'SUB_UMBRELLA' or cmd == 'SUB_CLIENT' or cmd == 'SUB_LIBRARY'): self.parseSubStuff(lc) elif cmd == 'TWOLEVEL_HINTS': self.parseTwoLevelHints(lc) elif cmd == 'PREBIND_CKSUM': self.parsePrebindCksum(lc) elif cmd == 'UUID': self.parseUUID(lc) elif (cmd == 'CODE_SIGNATURE' or cmd == 'SEGMENT_SPLIT_INFO' or cmd == 'FUNCTION_STARTS' or cmd == 'DATA_IN_CODE' or cmd == 'DYLIB_CODE_SIGN_DRS' or cmd == 'LINKER_OPTIMIZATION_HINT'): self.parseLinkedITData(lc) elif cmd == 'ENCRYPTION_INFO' or cmd == 'ENCRYPTION_INFO_64': self.parseEncryptionInfo(lc) elif cmd == 'DYLD_INFO' or cmd == 'DYLD_INFO_ONLY': self.parseDyldInfo(lc) elif (cmd == 'VERSION_MIN_MACOSX' or cmd == 'VERSION_MIN_IPHONEOS' or cmd == 'VERSION_MIN_WATCHOS'): self.parseVersionMinOS(lc) elif cmd == 'SOURCE_VERSION': self.parseSourceVersion(lc) elif cmd == 'LINKER_OPTION': self.parseLinkerOption(lc) elif cmd == 'RPATH': self.parseRPath(lc) elif cmd == 'MAIN': self.parseMain(lc)