1/* 2 * Copyright (c) 2006-2014 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24// 25// macho++ - Mach-O object file helpers 26// 27#include "macho++.h" 28#include <security_utilities/alloc.h> 29#include <security_utilities/memutils.h> 30#include <security_utilities/endian.h> 31#include <mach-o/dyld.h> 32#include <list> 33#include <algorithm> 34#include <iterator> 35 36namespace Security { 37 38/* Maximum number of archs a fat binary can have */ 39static const int MAX_ARCH_COUNT = 100; 40/* Maximum power of 2 that a mach-o can be aligned by */ 41static const int MAX_ALIGN = 30; 42 43// 44// Architecture values 45// 46Architecture::Architecture(const fat_arch &arch) 47 : pair<cpu_type_t, cpu_subtype_t>(arch.cputype, arch.cpusubtype) 48{ 49} 50 51Architecture::Architecture(const char *name) 52{ 53 if (const NXArchInfo *nxa = NXGetArchInfoFromName(name)) { 54 this->first = nxa->cputype; 55 this->second = nxa->cpusubtype; 56 } else { 57 this->first = this->second = none; 58 } 59} 60 61 62// 63// The local architecture. 64// 65// We take this from ourselves - the architecture of our main program Mach-O binary. 66// There's the NXGetLocalArchInfo API, but it insists on saying "i386" on modern 67// x86_64-centric systems, and lies to ppc (Rosetta) programs claiming they're native ppc. 68// So let's not use that. 69// 70Architecture Architecture::local() 71{ 72 return MainMachOImage().architecture(); 73} 74 75 76// 77// Translate between names and numbers 78// 79const char *Architecture::name() const 80{ 81 if (const NXArchInfo *info = NXGetArchInfoFromCpuType(cpuType(), cpuSubtype())) 82 return info->name; 83 else 84 return NULL; 85} 86 87std::string Architecture::displayName() const 88{ 89 if (const char *s = this->name()) 90 return s; 91 char buf[20]; 92 snprintf(buf, sizeof(buf), "(%d:%d)", cpuType(), cpuSubtype()); 93 return buf; 94} 95 96 97// 98// Compare architectures. 99// This is asymmetrical; the second argument provides for some templating. 100// 101bool Architecture::matches(const Architecture &templ) const 102{ 103 if (first != templ.first) 104 return false; // main architecture mismatch 105 if (templ.second == CPU_SUBTYPE_MULTIPLE) 106 return true; // subtype wildcard 107 // match subtypes, ignoring feature bits 108 return ((second ^ templ.second) & ~CPU_SUBTYPE_MASK) == 0; 109} 110 111 112// 113// MachOBase contains knowledge of the Mach-O object file format, 114// but abstracts from any particular sourcing. It must be subclassed, 115// and the subclass must provide the file header and commands area 116// during its construction. Memory is owned by the subclass. 117// 118MachOBase::~MachOBase() 119{ /* virtual */ } 120 121// provide the Mach-O file header, somehow 122void MachOBase::initHeader(const mach_header *header) 123{ 124 mHeader = header; 125 switch (mHeader->magic) { 126 case MH_MAGIC: 127 mFlip = false; 128 m64 = false; 129 break; 130 case MH_CIGAM: 131 mFlip = true; 132 m64 = false; 133 break; 134 case MH_MAGIC_64: 135 mFlip = false; 136 m64 = true; 137 break; 138 case MH_CIGAM_64: 139 mFlip = true; 140 m64 = true; 141 break; 142 default: 143 secdebug("macho", "%p: unrecognized header magic (%x)", this, mHeader->magic); 144 UnixError::throwMe(ENOEXEC); 145 } 146} 147 148// provide the Mach-O commands section, somehow 149void MachOBase::initCommands(const load_command *commands) 150{ 151 mCommands = commands; 152 mEndCommands = LowLevelMemoryUtilities::increment<load_command>(commands, flip(mHeader->sizeofcmds)); 153 if (mCommands + 1 > mEndCommands) // ensure initial load command core available 154 UnixError::throwMe(ENOEXEC); 155} 156 157 158size_t MachOBase::headerSize() const 159{ 160 return m64 ? sizeof(mach_header_64) : sizeof(mach_header); 161} 162 163size_t MachOBase::commandSize() const 164{ 165 return flip(mHeader->sizeofcmds); 166} 167 168 169// 170// Create a MachO object from an open file and a starting offset. 171// We load (only) the header and load commands into memory at that time. 172// Note that the offset must be relative to the start of the containing file 173// (not relative to some intermediate container). 174// 175MachO::MachO(FileDesc fd, size_t offset, size_t length) 176 : FileDesc(fd), mOffset(offset), mLength(length), mSuspicious(false) 177{ 178 if (mOffset == 0) 179 mLength = fd.fileSize(); 180 size_t size = fd.read(&mHeaderBuffer, sizeof(mHeaderBuffer), mOffset); 181 if (size != sizeof(mHeaderBuffer)) 182 UnixError::throwMe(ENOEXEC); 183 this->initHeader(&mHeaderBuffer); 184 size_t cmdSize = this->commandSize(); 185 mCommandBuffer = (load_command *)malloc(cmdSize); 186 if (!mCommandBuffer) 187 UnixError::throwMe(); 188 if (fd.read(mCommandBuffer, cmdSize, this->headerSize() + mOffset) != cmdSize) 189 UnixError::throwMe(ENOEXEC); 190 this->initCommands(mCommandBuffer); 191 /* If we do not know the length, we cannot do a verification of the mach-o structure */ 192 if (mLength != 0) 193 this->validateStructure(); 194} 195 196void MachO::validateStructure() 197{ 198 bool isValid = false; 199 200 /* There should be either an LC_SEGMENT, an LC_SEGMENT_64, or an LC_SYMTAB 201 load_command and that + size must be equal to the end of the arch */ 202 for (const struct load_command *cmd = loadCommands(); cmd != NULL; cmd = nextCommand(cmd)) { 203 uint32_t cmd_type = flip(cmd->cmd); 204 struct segment_command *seg = NULL; 205 struct segment_command_64 *seg64 = NULL; 206 struct symtab_command *symtab = NULL; 207 208 if (cmd_type == LC_SEGMENT) { 209 seg = (struct segment_command *)cmd; 210 if (strcmp(seg->segname, SEG_LINKEDIT) == 0) { 211 isValid = flip(seg->fileoff) + flip(seg->filesize) == this->length(); 212 break; 213 } 214 } else if (cmd_type == LC_SEGMENT_64) { 215 seg64 = (struct segment_command_64 *)cmd; 216 if (strcmp(seg64->segname, SEG_LINKEDIT) == 0) { 217 isValid = flip(seg64->fileoff) + flip(seg64->filesize) == this->length(); 218 break; 219 } 220 /* PPC binaries have a SYMTAB section */ 221 } else if (cmd_type == LC_SYMTAB) { 222 symtab = (struct symtab_command *)cmd; 223 isValid = flip(symtab->stroff) + flip(symtab->strsize) == this->length(); 224 break; 225 } 226 } 227 228 if (!isValid) 229 mSuspicious = true; 230} 231 232MachO::~MachO() 233{ 234 ::free(mCommandBuffer); 235} 236 237 238// 239// Create a MachO object that is (entirely) mapped into memory. 240// The caller must ensire that the underlying mapping persists 241// at least as long as our object. 242// 243MachOImage::MachOImage(const void *address) 244{ 245 this->initHeader((const mach_header *)address); 246 this->initCommands(LowLevelMemoryUtilities::increment<const load_command>(address, this->headerSize())); 247} 248 249 250// 251// Locate the Mach-O image of the main program 252// 253MainMachOImage::MainMachOImage() 254 : MachOImage(mainImageAddress()) 255{ 256} 257 258const void *MainMachOImage::mainImageAddress() 259{ 260 return _dyld_get_image_header(0); 261} 262 263 264// 265// Return various header fields 266// 267Architecture MachOBase::architecture() const 268{ 269 return Architecture(flip(mHeader->cputype), flip(mHeader->cpusubtype)); 270} 271 272uint32_t MachOBase::type() const 273{ 274 return flip(mHeader->filetype); 275} 276 277uint32_t MachOBase::flags() const 278{ 279 return flip(mHeader->flags); 280} 281 282 283// 284// Iterate through load commands 285// 286const load_command *MachOBase::nextCommand(const load_command *command) const 287{ 288 using LowLevelMemoryUtilities::increment; 289 command = increment<const load_command>(command, flip(command->cmdsize)); 290 if (command >= mEndCommands) // end of load commands 291 return NULL; 292 if (increment(command, sizeof(load_command)) > mEndCommands 293 || increment(command, flip(command->cmdsize)) > mEndCommands) 294 UnixError::throwMe(ENOEXEC); 295 return command; 296} 297 298 299// 300// Find a specific load command, by command number. 301// If there are multiples, returns the first one found. 302// 303const load_command *MachOBase::findCommand(uint32_t cmd) const 304{ 305 for (const load_command *command = loadCommands(); command; command = nextCommand(command)) 306 if (flip(command->cmd) == cmd) 307 return command; 308 return NULL; 309} 310 311 312// 313// Locate a segment command, by name 314// 315const segment_command *MachOBase::findSegment(const char *segname) const 316{ 317 for (const load_command *command = loadCommands(); command; command = nextCommand(command)) { 318 switch (flip(command->cmd)) { 319 case LC_SEGMENT: 320 case LC_SEGMENT_64: 321 { 322 const segment_command *seg = reinterpret_cast<const segment_command *>(command); 323 if (!strcmp(seg->segname, segname)) 324 return seg; 325 break; 326 } 327 default: 328 break; 329 } 330 } 331 return NULL; 332} 333 334const section *MachOBase::findSection(const char *segname, const char *sectname) const 335{ 336 using LowLevelMemoryUtilities::increment; 337 if (const segment_command *seg = findSegment(segname)) { 338 if (is64()) { 339 const segment_command_64 *seg64 = reinterpret_cast<const segment_command_64 *>(seg); 340 // As a sanity check, if the reported number of sections is not consistent 341 // with the reported size, return NULL 342 if (sizeof(*seg64) + (seg64->nsects * sizeof(section_64)) > seg64->cmdsize) 343 return NULL; 344 const section_64 *sect = increment<const section_64>(seg64 + 1, 0); 345 for (unsigned n = flip(seg64->nsects); n > 0; n--, sect++) { 346 if (!strcmp(sect->sectname, sectname)) 347 return reinterpret_cast<const section *>(sect); 348 } 349 } else { 350 const section *sect = increment<const section>(seg + 1, 0); 351 for (unsigned n = flip(seg->nsects); n > 0; n--, sect++) { 352 if (!strcmp(sect->sectname, sectname)) 353 return sect; 354 } 355 } 356 } 357 return NULL; 358} 359 360 361// 362// Translate a union lc_str into the string it denotes. 363// Returns NULL (no exceptions) if the entry is corrupt. 364// 365const char *MachOBase::string(const load_command *cmd, const lc_str &str) const 366{ 367 size_t offset = flip(str.offset); 368 const char *sp = LowLevelMemoryUtilities::increment<const char>(cmd, offset); 369 if (offset + strlen(sp) + 1 > flip(cmd->cmdsize)) // corrupt string reference 370 return NULL; 371 return sp; 372} 373 374 375// 376// Figure out where the Code Signing information starts in the Mach-O binary image. 377// The code signature is at the end of the file, and identified 378// by a specially-named section. So its starting offset is also the end 379// of the signable part. 380// Note that the offset returned is relative to the start of the Mach-O image. 381// Returns zero if not found (usually indicating that the binary was not signed). 382// 383const linkedit_data_command *MachOBase::findCodeSignature() const 384{ 385 if (const load_command *cmd = findCommand(LC_CODE_SIGNATURE)) 386 return reinterpret_cast<const linkedit_data_command *>(cmd); 387 return NULL; // not found 388} 389 390size_t MachOBase::signingOffset() const 391{ 392 if (const linkedit_data_command *lec = findCodeSignature()) 393 return flip(lec->dataoff); 394 else 395 return 0; 396} 397 398size_t MachOBase::signingLength() const 399{ 400 if (const linkedit_data_command *lec = findCodeSignature()) 401 return flip(lec->datasize); 402 else 403 return 0; 404} 405 406const linkedit_data_command *MachOBase::findLibraryDependencies() const 407{ 408 if (const load_command *cmd = findCommand(LC_DYLIB_CODE_SIGN_DRS)) 409 return reinterpret_cast<const linkedit_data_command *>(cmd); 410 return NULL; // not found 411} 412 413 414// 415// Return the signing-limit length for this Mach-O binary image. 416// This is the signingOffset if present, or the full length if not. 417// 418size_t MachO::signingExtent() const 419{ 420 if (size_t offset = signingOffset()) 421 return offset; 422 else 423 return length(); 424} 425 426 427// 428// I/O operations 429// 430void MachO::seek(size_t offset) 431{ 432 FileDesc::seek(mOffset + offset); 433} 434 435CFDataRef MachO::dataAt(size_t offset, size_t size) 436{ 437 CFMallocData buffer(size); 438 if (this->read(buffer, size, mOffset + offset) != size) 439 UnixError::throwMe(); 440 return buffer; 441} 442 443// 444// Fat (aka universal) file wrappers. 445// The offset is relative to the start of the containing file. 446// 447Universal::Universal(FileDesc fd, size_t offset /* = 0 */, size_t length /* = 0 */) 448 : FileDesc(fd), mBase(offset), mLength(length), mSuspicious(false) 449{ 450 union { 451 fat_header header; // if this is a fat file 452 mach_header mheader; // if this is a thin file 453 }; 454 const size_t size = max(sizeof(header), sizeof(mheader)); 455 if (fd.read(&header, size, offset) != size) 456 UnixError::throwMe(ENOEXEC); 457 switch (header.magic) { 458 case FAT_MAGIC: 459 case FAT_CIGAM: 460 { 461 // 462 // Hack alert. 463 // Under certain circumstances (15001604), mArchCount under-counts the architectures 464 // by one, and special testing is required to validate the extra-curricular entry. 465 // We always read an extra entry; in the situations where this might hit end-of-file, 466 // we are content to fail. 467 // 468 mArchCount = ntohl(header.nfat_arch); 469 470 if (mArchCount > MAX_ARCH_COUNT) 471 UnixError::throwMe(ENOEXEC); 472 473 size_t archSize = sizeof(fat_arch) * (mArchCount + 1); 474 mArchList = (fat_arch *)malloc(archSize); 475 if (!mArchList) 476 UnixError::throwMe(); 477 if (fd.read(mArchList, archSize, mBase + sizeof(header)) != archSize) { 478 ::free(mArchList); 479 UnixError::throwMe(ENOEXEC); 480 } 481 for (fat_arch *arch = mArchList; arch <= mArchList + mArchCount; arch++) { 482 n2hi(arch->cputype); 483 n2hi(arch->cpusubtype); 484 n2hi(arch->offset); 485 n2hi(arch->size); 486 n2hi(arch->align); 487 } 488 const fat_arch *last_arch = mArchList + mArchCount; 489 if (last_arch->cputype == (CPU_ARCH_ABI64 | CPU_TYPE_ARM)) { 490 mArchCount++; 491 } 492 secdebug("macho", "%p is a fat file with %d architectures", 493 this, mArchCount); 494 495 /* A Mach-O universal file has padding of no more than "page size" 496 * between the header and slices. This padding must be zeroed out or the file 497 is not valid */ 498 std::list<struct fat_arch *> sortedList; 499 for (unsigned i = 0; i < mArchCount; i++) 500 sortedList.push_back(mArchList + i); 501 502 sortedList.sort(^ bool (const struct fat_arch *arch1, const struct fat_arch *arch2) { return arch1->offset < arch2->offset; }); 503 504 const size_t universalHeaderEnd = mBase + sizeof(header) + (sizeof(fat_arch) * mArchCount); 505 size_t prevHeaderEnd = universalHeaderEnd; 506 size_t prevArchSize = 0, prevArchStart = 0; 507 508 for (auto iterator = sortedList.begin(); iterator != sortedList.end(); ++iterator) { 509 auto ret = mSizes.insert(std::pair<size_t, size_t>((*iterator)->offset, (*iterator)->size)); 510 if (ret.second == false) { 511 ::free(mArchList); 512 MacOSError::throwMe(errSecInternalError); // Something is wrong if the same size was encountered twice 513 } 514 515 size_t gapSize = (*iterator)->offset - prevHeaderEnd; 516 517 /* The size of the padding after the universal cannot be calculated to a fixed size */ 518 if (prevHeaderEnd != universalHeaderEnd) { 519 if (((*iterator)->align > MAX_ALIGN) || gapSize >= (1 << (*iterator)->align)) { 520 mSuspicious = true; 521 break; 522 } 523 } 524 525 // validate gap bytes in tasty page-sized chunks 526 CssmAutoPtr<uint8_t> gapBytes(Allocator::standard().malloc<uint8_t>(PAGE_SIZE)); 527 size_t off = 0; 528 while (off < gapSize) { 529 size_t want = min(gapSize - off, (size_t)PAGE_SIZE); 530 size_t got = fd.read(gapBytes, want, prevHeaderEnd + off); 531 off += got; 532 for (size_t x = 0; x < got; x++) { 533 if (gapBytes[x] != 0) { 534 mSuspicious = true; 535 break; 536 } 537 } 538 if (mSuspicious) 539 break; 540 } 541 if (off != gapSize) 542 mSuspicious = true; 543 if (mSuspicious) 544 break; 545 546 prevHeaderEnd = (*iterator)->offset + (*iterator)->size; 547 prevArchSize = (*iterator)->size; 548 prevArchStart = (*iterator)->offset; 549 } 550 551 /* If there is anything extra at the end of the file, reject this */ 552 if (!mSuspicious && (prevArchStart + prevArchSize != fd.fileSize())) 553 mSuspicious = true; 554 555 break; 556 } 557 case MH_MAGIC: 558 case MH_MAGIC_64: 559 mArchList = NULL; 560 mArchCount = 0; 561 mThinArch = Architecture(mheader.cputype, mheader.cpusubtype); 562 secdebug("macho", "%p is a thin file (%s)", this, mThinArch.name()); 563 break; 564 case MH_CIGAM: 565 case MH_CIGAM_64: 566 mArchList = NULL; 567 mArchCount = 0; 568 mThinArch = Architecture(flip(mheader.cputype), flip(mheader.cpusubtype)); 569 secdebug("macho", "%p is a thin file (%s)", this, mThinArch.name()); 570 break; 571 default: 572 UnixError::throwMe(ENOEXEC); 573 } 574} 575 576Universal::~Universal() 577{ 578 ::free(mArchList); 579} 580 581const size_t Universal::lengthOfSlice(size_t offset) const 582{ 583 auto ret = mSizes.find(offset); 584 if (ret == mSizes.end()) 585 MacOSError::throwMe(errSecInternalError); 586 return ret->second; 587} 588 589// 590// Get the "local" architecture from the fat file 591// Throws ENOEXEC if not found. 592// 593MachO *Universal::architecture() const 594{ 595 if (isUniversal()) 596 return findImage(bestNativeArch()); 597 else 598 return new MachO(*this, mBase, mLength); 599} 600 601size_t Universal::archOffset() const 602{ 603 if (isUniversal()) 604 return mBase + findArch(bestNativeArch())->offset; 605 else 606 return mBase; 607} 608 609 610// 611// Get the specified architecture from the fat file 612// Throws ENOEXEC if not found. 613// 614MachO *Universal::architecture(const Architecture &arch) const 615{ 616 if (isUniversal()) 617 return findImage(arch); 618 else if (mThinArch.matches(arch)) 619 return new MachO(*this, mBase); 620 else 621 UnixError::throwMe(ENOEXEC); 622} 623 624size_t Universal::archOffset(const Architecture &arch) const 625{ 626 if (isUniversal()) 627 return mBase + findArch(arch)->offset; 628 else if (mThinArch.matches(arch)) 629 return 0; 630 else 631 UnixError::throwMe(ENOEXEC); 632} 633 634size_t Universal::archLength(const Architecture &arch) const 635{ 636 if (isUniversal()) 637 return mBase + findArch(arch)->size; 638 else if (mThinArch.matches(arch)) 639 return this->fileSize(); 640 else 641 UnixError::throwMe(ENOEXEC); 642} 643 644// 645// Get the architecture at a specified offset from the fat file. 646// Throws an exception of the offset does not point at a Mach-O image. 647// 648MachO *Universal::architecture(size_t offset) const 649{ 650 if (isUniversal()) 651 return new MachO(*this, offset); 652 else if (offset == mBase) 653 return new MachO(*this); 654 else 655 UnixError::throwMe(ENOEXEC); 656} 657 658 659// 660// Locate an architecture from the fat file's list. 661// Throws ENOEXEC if not found. 662// 663const fat_arch *Universal::findArch(const Architecture &target) const 664{ 665 assert(isUniversal()); 666 const fat_arch *end = mArchList + mArchCount; 667 // exact match 668 for (const fat_arch *arch = mArchList; arch < end; ++arch) 669 if (arch->cputype == target.cpuType() 670 && arch->cpusubtype == target.cpuSubtype()) 671 return arch; 672 // match for generic model of main architecture 673 for (const fat_arch *arch = mArchList; arch < end; ++arch) 674 if (arch->cputype == target.cpuType() && arch->cpusubtype == 0) 675 return arch; 676 // match for any subarchitecture of the main architecture (questionable) 677 for (const fat_arch *arch = mArchList; arch < end; ++arch) 678 if (arch->cputype == target.cpuType()) 679 return arch; 680 // no match 681 UnixError::throwMe(ENOEXEC); // not found 682} 683 684MachO *Universal::findImage(const Architecture &target) const 685{ 686 const fat_arch *arch = findArch(target); 687 return new MachO(*this, mBase + arch->offset, arch->size); 688} 689 690 691// 692// Find the best-matching architecture for this fat file. 693// We pick the native architecture if it's available. 694// If it contains exactly one architecture, we take that. 695// Otherwise, we throw. 696// 697Architecture Universal::bestNativeArch() const 698{ 699 if (isUniversal()) { 700 // ask the NXArch API for our native architecture 701 const Architecture native = Architecture::local(); 702 if (fat_arch *match = NXFindBestFatArch(native.cpuType(), native.cpuSubtype(), mArchList, mArchCount)) 703 return *match; 704 // if the system can't figure it out, pick (arbitrarily) the first one 705 return mArchList[0]; 706 } else 707 return mThinArch; 708} 709 710// 711// List all architectures from the fat file's list. 712// 713void Universal::architectures(Architectures &archs) const 714{ 715 if (isUniversal()) { 716 for (unsigned n = 0; n < mArchCount; n++) 717 archs.insert(mArchList[n]); 718 } else { 719 auto_ptr<MachO> macho(architecture()); 720 archs.insert(macho->architecture()); 721 } 722} 723 724// 725// Quickly guess the Mach-O type of a file. 726// Returns type zero if the file isn't Mach-O or Universal. 727// Always looks at the start of the file, and does not change the file pointer. 728// 729uint32_t Universal::typeOf(FileDesc fd) 730{ 731 mach_header header; 732 int max_tries = 3; 733 if (fd.read(&header, sizeof(header), 0) != sizeof(header)) 734 return 0; 735 while (max_tries > 0) { 736 switch (header.magic) { 737 case MH_MAGIC: 738 case MH_MAGIC_64: 739 return header.filetype; 740 break; 741 case MH_CIGAM: 742 case MH_CIGAM_64: 743 return flip(header.filetype); 744 break; 745 case FAT_MAGIC: 746 case FAT_CIGAM: 747 { 748 const fat_arch *arch1 = 749 LowLevelMemoryUtilities::increment<fat_arch>(&header, sizeof(fat_header)); 750 if (fd.read(&header, sizeof(header), ntohl(arch1->offset)) != sizeof(header)) 751 return 0; 752 max_tries--; 753 continue; 754 } 755 default: 756 return 0; 757 } 758 } 759 return 0; 760} 761 762// 763// Strict validation 764// 765bool Universal::isSuspicious() const 766{ 767 if (mSuspicious) 768 return true; 769 Universal::Architectures archList; 770 architectures(archList); 771 for (Universal::Architectures::const_iterator it = archList.begin(); it != archList.end(); ++it) { 772 auto_ptr<MachO> macho(architecture(*it)); 773 if (macho->isSuspicious()) 774 return true; 775 } 776 return false; 777} 778 779 780} // Security 781