2015-04-13 5 views
1

Как я могу гарантировать целостность кода приложения iOS? Я смотрю на документ AppleSecurity Overview, будет ли достаточно подписания кода? Есть ли другой рекомендуемый механизм, гарантирующий целостность кода?Проверка целостности кода в iOS

Заранее спасибо

+0

приложения компилируются в двоичные файлы, зашифрованные и подписанные. Это достаточно безопасно для авторских прав, и большинство пользователей могут попробовать и сбросить заголовки. – Schemetrical

+0

Вы пытаетесь проверить целостность своего кода во время выполнения? Если его просто * «гарантирует целостность кода» *, вы его кодируете. ОС будет применять требования к целостности до того, как загрузит приложение. Это означает, что вы не используете джейлбрейк, который отключает подпись гейткипера/кода (джейлбрейки, которые * могут * использовать 'ldid', отключить службу и вставить поддельную подпись. Если вы * не можете * использовать' ldid', JB не отключил службу гейткипера). – jww

+0

@jww Мне также нужно проверить целостность кода после установки приложения ... так, как я мог точно проверить это? – AppsDev

ответ

2

У меня была такая же проблема. Это легко в OS X, но несколько сложно в iOS, потому что у iOS нет API, например, SecStaticCodeCheckValidity.

Существует два раздела в двоичном формате mach-o, которые можно использовать для обеспечения целостности приложения.

  1. LC_ENCRYPTION_INFO
  2. LC_CODE_SIGNATURE

1. LC_ENCRYPTION_INFO

Во-первых, LC_ENCRYPTION_INFO хранит информацию о 'магазина приложений шифрования'. Когда приложение загружается в магазин приложений, приложение зашифровывается, прежде чем оно будет выпущено пользователям.

двоичных перед загрузкой в ​​AppStore или расшифрованы

otool -l [binary] | grep LC_ENCRYPTION_INFO -A5 
      cmd LC_ENCRYPTION_INFO 
     cmdsize 20 
    cryptoff 16384 
    cryptsize 5783552 
     cryptid 0 
-- 
      cmd LC_ENCRYPTION_INFO_64 
     cmdsize 24 
    cryptoff 16384 
    cryptsize 6635520 
     cryptid 0 
      pad 0 

бинарных после загрузки в AppStore (зашифровано)

otool -l [binary] | grep LC_ENCRYPTION_INFO -A5 
      cmd LC_ENCRYPTION_INFO 
     cmdsize 20 
    cryptoff 16384 
    cryptsize 5783552 
     cryptid 1 
-- 
      cmd LC_ENCRYPTION_INFO_64 
     cmdsize 24 
    cryptoff 16384 
    cryptsize 6635520 
     cryptid 1 
      pad 0 

Как вы можете видеть, 'Cryptid' устанавливается равным 1, если приложение загружено. Поэтому проверка «криптоватого» бита подскажет нам, зашифрован ли двоичный файл или нет.

Вы можете подумать, что это можно легко обойти, просто установив бит в 1, но затем ОС попытается расшифровать двоичный код, который сделает коды неузнаваемыми байтами.

bool isBinaryEncrypted() 
{ 
    // checking current binary's LC_ENCRYPTION_INFO 
    const void *binaryBase; 
    struct load_command *machoCmd; 
    const struct mach_header *machoHeader; 

    NSString *path = [[NSBundle mainBundle] executablePath]; 
    NSData *filedata = [NSData dataWithContentsOfFile:path]; 
    binaryBase = (char *)[filedata bytes]; 

    machoHeader = (const struct mach_header *) binaryBase; 

    if(machoHeader->magic == FAT_CIGAM) 
    { 
     unsigned int offset = 0; 
     struct fat_arch *fatArch = (struct fat_arch *)((struct fat_header *)machoHeader + 1); 
     struct fat_header *fatHeader = (struct fat_header *)machoHeader; 
     for(uint32_t i = 0; i < ntohl(fatHeader->nfat_arch); i++) 
     { 
      if(sizeof(int *) == 4 && !(ntohl(fatArch->cputype) & CPU_ARCH_ABI64)) // check 32bit section for 32bit architecture 
      { 
       offset = ntohl(fatArch->offset); 
       break; 
      } 
      else if(sizeof(int *) == 8 && (ntohl(fatArch->cputype) & CPU_ARCH_ABI64)) // and 64bit section for 64bit architecture 
      { 
       offset = ntohl(fatArch->offset); 
       break; 
      } 
      fatArch = (struct fat_arch *)((uint8_t *)fatArch + sizeof(struct fat_arch)); 
     } 
     machoHeader = (const struct mach_header *)((uint8_t *)machoHeader + offset); 
    } 
    if(machoHeader->magic == MH_MAGIC) // 32bit 
    { 
     machoCmd = (struct load_command *)((struct mach_header *)machoHeader + 1); 
    } 
    else if(machoHeader->magic == MH_MAGIC_64) // 64bit 
    { 
     machoCmd = (struct load_command *)((struct mach_header_64 *)machoHeader + 1); 
    } 
    for(uint32_t i=0; i < machoHeader->ncmds && machoCmd != NULL; i++){ 
     if(machoCmd->cmd == LC_ENCRYPTION_INFO) 
     { 
      struct encryption_info_command *cryptCmd = (struct encryption_info_command *) machoCmd; 
      return cryptCmd->cryptid; 
     } 
     if(machoCmd->cmd == LC_ENCRYPTION_INFO_64) 
     { 
      struct encryption_info_command_64 *cryptCmd = (struct encryption_info_command_64 *) machoCmd; 
      return cryptCmd->cryptid; 
     } 
     machoCmd = (struct load_command *)((uint8_t *)machoCmd + machoCmd->cmdsize); 
    } 
    return FALSE; // couldn't find cryptcmd 
} 

2. LC_CODE_SIGNATURE

LC_CODE_SIGNATURE является раздел,/USR/бен/CodeSign на самом деле относится при проверке достоверности двоичного кода. Но анализ раздела немного сложнее, чем разбор LC_ENCRYPTION_INFO, поскольку он недокументирован и нет таких типов, как signature_info_command.

LC_CODE_SIGNATURE содержит хэши всех двоичных файлов, кроме самой секции, и хеши корректируются всякий раз, когда они переписываются.

Я портировал коды/usr/bin/codesign для анализа этого раздела. check here и SecStaticCode :: validateExecutable определено в here

CodeSigning.h

#ifndef CodeSigning_h 
#define CodeSigning_h 

#include <stdio.h> 

// codes from https://opensource.apple.com/source/Security/Security-55179.1/libsecurity_codesigning/lib/cscdefs.h 

enum { 
    CSMAGIC_REQUIREMENT = 0xfade0c00,  /* single Requirement blob */ 
    CSMAGIC_REQUIREMENTS = 0xfade0c01,  /* Requirements vector (internal requirements) */ 
    CSMAGIC_CODEDIRECTORY = 0xfade0c02,  /* CodeDirectory blob */ 
    CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0, /* embedded form of signature data */ 
    CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1, /* multi-arch collection of embedded signatures */ 

    CSSLOT_CODEDIRECTORY = 0,    /* slot index for CodeDirectory */ 
}; 
/* 
* Structure of an embedded-signature SuperBlob 
*/ 
typedef struct __BlobIndex { 
    uint32_t type;     /* type of entry */ 
    uint32_t offset;    /* offset of entry */ 
} CS_BlobIndex; 

typedef struct __SuperBlob { 
    uint32_t magic;     /* magic number */ 
    uint32_t length;    /* total length of SuperBlob */ 
    uint32_t count;     /* number of index entries following */ 
    CS_BlobIndex index[];   /* (count) entries */ 
    /* followed by Blobs in no particular order as indicated by offsets in index */ 
} CS_SuperBlob; 


/* 
* C form of a CodeDirectory. 
*/ 
typedef struct __CodeDirectory { 
    uint32_t magic;     /* magic number (CSMAGIC_CODEDIRECTORY) */ 
    uint32_t length;    /* total length of CodeDirectory blob */ 
    uint32_t version;    /* compatibility version */ 
    uint32_t flags;     /* setup and mode flags */ 
    uint32_t hashOffset;   /* offset of hash slot element at index zero */ 
    uint32_t identOffset;   /* offset of identifier string */ 
    uint32_t nSpecialSlots;   /* number of special hash slots */ 
    uint32_t nCodeSlots;   /* number of ordinary (code) hash slots */ 
    uint32_t codeLimit;    /* limit to main image signature range */ 
    uint8_t hashSize;    /* size of each hash in bytes */ 
    uint8_t hashType;    /* type of hash (cdHashType* constants) */ 
    uint8_t spare1;     /* unused (must be zero) */ 
    uint8_t pageSize;    /* log2(page size in bytes); 0 => infinite */ 
    uint32_t spare2;    /* unused (must be zero) */ 
    /* followed by dynamic content as located by offset fields above */ 
} CS_CodeDirectory; 

static inline const CS_CodeDirectory *findCodeDirectory(const CS_SuperBlob *embedded) 
{ 
    if (embedded && ntohl(embedded->magic) == CSMAGIC_EMBEDDED_SIGNATURE) { 
     const CS_BlobIndex *limit = &embedded->index[ntohl(embedded->count)]; 
     const CS_BlobIndex *p; 
     for (p = embedded->index; p < limit; ++p) 
      if (ntohl(p->type) == CSSLOT_CODEDIRECTORY) { 
       const unsigned char *base = (const unsigned char *)embedded; 
       const CS_CodeDirectory *cd = (const CS_CodeDirectory *)(base + ntohl(p->offset)); 
       if (ntohl(cd->magic) == CSMAGIC_CODEDIRECTORY){ 
        return cd; 
       } 
       else{ 
        break; 
       } 
      } 
    } 
    // not found 
    return NULL; 
} 

// 
unsigned char validateSlot(const void *data, size_t length, size_t slot, const CS_CodeDirectory *codeDirectory); 
#endif /* CodeSigning_h */ 

CodeSigning.с

#include "CodeSigning.h" 
#include <stdio.h> 
#include <string.h> 
#import <CommonCrypto/CommonDigest.h> 
unsigned char validateSlot(const void *data, size_t length, size_t slot, const CS_CodeDirectory *codeDirectory) 
{ 
    uint8_t digest[CC_SHA1_DIGEST_LENGTH + 1] = {0, }; 
    CC_SHA1(data, (CC_LONG)length, digest); 
    return (memcmp(digest, (void *)((char *)codeDirectory + ntohl(codeDirectory->hashOffset) + 20*slot), 20) == 0); 
} 

разборе раздел

void checkCodeSignature(void *binaryContent){ 
    struct load_command *machoCmd; 
    const struct mach_header *machoHeader; 

    machoHeader = (const struct mach_header *) binaryContent; 
    if(machoHeader->magic == FAT_CIGAM){ 
     unsigned int offset = 0; 
     struct fat_arch *fatArch = (struct fat_arch *)((struct fat_header *)machoHeader + 1); 
     struct fat_header *fatHeader = (struct fat_header *)machoHeader; 
     for(uint32_t i = 0; i < ntohl(fatHeader->nfat_arch); i++) 
     { 
      if(sizeof(int *) == 4 && !(ntohl(fatArch->cputype) & CPU_ARCH_ABI64)) // check 32bit section for 32bit architecture 
      { 
       offset = ntohl(fatArch->offset); 
       break; 
      } 
      else if(sizeof(int *) == 8 && (ntohl(fatArch->cputype) & CPU_ARCH_ABI64)) // and 64bit section for 64bit architecture 
      { 
       offset = ntohl(fatArch->offset); 
       break; 
      } 
      fatArch = (struct fat_arch *)((uint8_t *)fatArch + sizeof(struct fat_arch)); 
     } 
     machoHeader = (const struct mach_header *)((uint8_t *)machoHeader + offset); 
    } 
    if(machoHeader->magic == MH_MAGIC) // 32bit 
    { 
     machoCmd = (struct load_command *)((struct mach_header *)machoHeader + 1); 
    } 
    else if(machoHeader->magic == MH_MAGIC_64) // 64bit 
    { 
     machoCmd = (struct load_command *)((struct mach_header_64 *)machoHeader + 1); 
    } 
    for(uint32_t i=0; i < machoHeader->ncmds && machoCmd != NULL; i++){ 
     if(machoCmd->cmd == LC_CODE_SIGNATURE) 
     { 
      struct linkedit_data_command *codeSigCmd = (struct linkedit_data_command *) machoCmd; 

      const CS_SuperBlob *codeEmbedded = (const CS_SuperBlob *)&((char *)machoHeader)[codeSigCmd->dataoff]; 
      void *binaryBase = (void *)machoHeader; 

      const CS_BlobIndex curIndex = codeEmbedded->index[0]; 
      const CS_CodeDirectory *codeDirectory = (const CS_CodeDirectory *)((char *)codeEmbedded + ntohl(curIndex.offset)); 

      size_t pageSize = codeDirectory->pageSize ? (1 << codeDirectory->pageSize) : 0; 
      size_t remaining = ntohl(codeDirectory->codeLimit); 
      size_t processed = 0; 
      for(size_t slot = 0; slot < ntohl(codeDirectory->nCodeSlots); ++slot){ 
       size_t size = MIN(remaining, pageSize); 
       if(!validateSlot(binaryBase+processed, size, slot, codeDirectory)){ 
        return; 
       } 
       processed += size; 
       remaining -= size; 
      } 
      printf("[*] Code is valid!"); 
     } 
    } 
    machoCmd = (struct load_command *)((uint8_t *)machoCmd + machoCmd->cmdsize); 
} 
Смежные вопросы