#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
-#include <mach-o/loader.h>
-#include <mach-o/fat.h>
-#include <mach-o/stab.h>
-#include <mach-o/reloc.h>
-#include <mach-o/ppc/reloc.h>
-#include <mach-o/x86_64/reloc.h>
#include <vector>
#include <set>
return false;
}
+template <>
+bool MachOChecker<arm>::validFile(const uint8_t* fileContent)
+{
+ const macho_header<P>* header = (const macho_header<P>*)fileContent;
+ if ( header->magic() != MH_MAGIC )
+ return false;
+ if ( header->cputype() != CPU_TYPE_ARM )
+ return false;
+ switch (header->filetype()) {
+ case MH_EXECUTE:
+ case MH_DYLIB:
+ case MH_BUNDLE:
+ case MH_DYLINKER:
+ return true;
+ }
+ return false;
+}
template <> uint8_t MachOChecker<ppc>::loadCommandSizeMask() { return 0x03; }
template <> uint8_t MachOChecker<ppc64>::loadCommandSizeMask() { return 0x07; }
template <> uint8_t MachOChecker<x86>::loadCommandSizeMask() { return 0x03; }
template <> uint8_t MachOChecker<x86_64>::loadCommandSizeMask() { return 0x07; }
-
+template <> uint8_t MachOChecker<arm>::loadCommandSizeMask() { return 0x03; }
template <typename A>
MachOChecker<A>::MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path)
void MachOChecker<A>::checkLoadCommands()
{
// check that all load commands fit within the load command space file
+ const macho_encryption_info_command<P>* encryption_info = NULL;
const uint8_t* const endOfFile = (uint8_t*)fHeader + fLength;
const uint8_t* const endOfLoadCommands = (uint8_t*)fHeader + sizeof(macho_header<P>) + fHeader->sizeofcmds();
const uint32_t cmd_count = fHeader->ncmds();
case LC_TWOLEVEL_HINTS:
case LC_PREBIND_CKSUM:
case LC_LOAD_WEAK_DYLIB:
+ case LC_LAZY_LOAD_DYLIB:
case LC_UUID:
case LC_REEXPORT_DYLIB:
case LC_SEGMENT_SPLIT_INFO:
+ case LC_CODE_SIGNATURE:
+ break;
+ case LC_ENCRYPTION_INFO:
+ encryption_info = (macho_encryption_info_command<P>*)cmd;
break;
case LC_SUB_UMBRELLA:
case LC_SUB_LIBRARY:
throwf("section %s vm address not within segment", sect->sectname());
if ( (sect->addr()+sect->size()) > endAddr )
throwf("section %s vm address not within segment", sect->sectname());
- if ( ((sect->flags() &SECTION_TYPE) != S_ZEROFILL) && (segCmd->filesize() != 0) ) {
+ if ( ((sect->flags() & SECTION_TYPE) != S_ZEROFILL) && (segCmd->filesize() != 0) ) {
if ( sect->offset() < startOffset )
throwf("section %s file offset not within segment", sect->sectname());
if ( (sect->offset()+sect->size()) > endOffset )
}
}
+ // verify encryption info
+ if ( encryption_info != NULL ) {
+ if ( fHeader->filetype() != MH_EXECUTE )
+ throw "LC_ENCRYPTION_INFO load command is only legal in main executables";
+ if ( encryption_info->cryptoff() < (sizeof(macho_header<P>) + fHeader->sizeofcmds()) )
+ throw "LC_ENCRYPTION_INFO load command has cryptoff covers some load commands";
+ if ( (encryption_info->cryptoff() % 4096) != 0 )
+ throw "LC_ENCRYPTION_INFO load command has cryptoff which is not page aligned";
+ if ( (encryption_info->cryptsize() % 4096) != 0 )
+ throw "LC_ENCRYPTION_INFO load command has cryptsize which is not page sized";
+ for (typename std::vector<std::pair<pint_t, pint_t> >::iterator it = segmentFileOffsetRanges.begin();
+ it != segmentFileOffsetRanges.end(); ++it) {
+ if ( (it->first <= encryption_info->cryptoff()) && (encryption_info->cryptoff() < it->second) ) {
+ if ( (encryption_info->cryptoff() + encryption_info->cryptsize()) > it->second )
+ throw "LC_ENCRYPTION_INFO load command is not contained within one segment";
+ }
+ }
+ }
+
// check LC_SYMTAB, LC_DYSYMTAB, and LC_SEGMENT_SPLIT_INFO
cmd = cmds;
bool foundDynamicSymTab = false;
return fFirstWritableSegment->vmaddr();
}
+template <>
+arm::P::uint_t MachOChecker<arm>::relocBase()
+{
+ if ( fHeader->flags() & MH_SPLIT_SEGS )
+ return fFirstWritableSegment->vmaddr();
+ else
+ return fFirstSegment->vmaddr();
+}
+
template <typename A>
bool MachOChecker<A>::addressInWritableSegment(pint_t address)
for (uint32_t i = 0; i < cmd_count; ++i) {
if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd;
- if ( (segCmd->initprot() & VM_PROT_WRITE) != 0 ) {
- if ( (address >= segCmd->vmaddr()) && (address < segCmd->vmaddr()+segCmd->vmsize()) )
+ if ( (address >= segCmd->vmaddr()) && (address < segCmd->vmaddr()+segCmd->vmsize()) ) {
+ // if segment is writable, we are fine
+ if ( (segCmd->initprot() & VM_PROT_WRITE) != 0 )
return true;
+ // could be a text reloc, make sure section bit is set
+ const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
+ const macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()];
+ for(const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
+ if ( (sect->addr() <= address) && (address < (sect->addr()+sect->size())) ) {
+ // found section for this address, if has relocs we are fine
+ return ( (sect->flags() & (S_ATTR_EXT_RELOC|S_ATTR_LOC_RELOC)) != 0 );
+ }
+ }
}
}
cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
// FIX: check r_symbol
}
+template <>
+void MachOChecker<arm>::checkExternalReloation(const macho_relocation_info<P>* reloc)
+{
+ if ( reloc->r_length() != 2 )
+ throw "bad external relocation length";
+ if ( reloc->r_type() != ARM_RELOC_VANILLA )
+ throw "unknown external relocation type";
+ if ( reloc->r_pcrel() != 0 )
+ throw "bad external relocation pc_rel";
+ if ( reloc->r_extern() == 0 )
+ throw "local relocation found with external relocations";
+ if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) )
+ throw "external relocation address not in writable segment";
+ // FIX: check r_symbol
+}
+
+
template <>
void MachOChecker<ppc>::checkLocalReloation(const macho_relocation_info<P>* reloc)
{
}
else {
+ // ignore pair relocs
+ if ( reloc->r_type() == PPC_RELOC_PAIR )
+ return;
// FIX
if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) )
- throw "local relocation address not in writable segment";
+ throwf("local relocation address 0x%08X not in writable segment", reloc->r_address());
}
}
throw "local relocation address not in writable segment";
}
-
+template <>
+void MachOChecker<arm>::checkLocalReloation(const macho_relocation_info<P>* reloc)
+{
+ if ( reloc->r_address() & R_SCATTERED ) {
+ // scattered
+ const macho_scattered_relocation_info<P>* sreloc = (const macho_scattered_relocation_info<P>*)reloc;
+ if ( sreloc->r_length() != 2 )
+ throw "bad local scattered relocation length";
+ if ( sreloc->r_type() != ARM_RELOC_PB_LA_PTR )
+ throw "bad local scattered relocation type";
+ }
+ else {
+ if ( reloc->r_length() != 2 )
+ throw "bad local relocation length";
+ if ( reloc->r_extern() != 0 )
+ throw "external relocation found with local relocations";
+ if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) )
+ throw "local relocation address not in writable segment";
+ }
+}
template <typename A>
void MachOChecker<A>::checkRelocations()
else
throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
break;
+ case CPU_TYPE_ARM:
+ if ( MachOChecker<arm>::validFile(p + offset) )
+ MachOChecker<arm>::make(p + offset, size, path);
+ else
+ throw "in universal file, arm slice does not contain arm mach-o";
+ break;
default:
throwf("in universal file, unknown architecture slice 0x%x\n", cputype);
}
else if ( MachOChecker<x86_64>::validFile(p) ) {
MachOChecker<x86_64>::make(p, length, path);
}
+ else if ( MachOChecker<arm>::validFile(p) ) {
+ MachOChecker<arm>::make(p, length, path);
+ }
else {
throw "not a known file type";
}