检查iOS中的代码完整性

时间:2015-04-13 05:19:15

标签: ios security code-signing integrity

我如何保证iOS应用代码的完整性?我一直在查看Apple的{​​{3}}文档,代码签名是否足够?是否有其他推荐的机制来保证代码的完整性?

提前致谢

1 个答案:

答案 0 :(得分:7)

我遇到了同样的问题。这在OS X上很容易,但在iOS中有点困难,因为iOS没有像SecStaticCodeCheckValidity这样的API。

可以使用mach-o二进制文件中的两个部分来确保应用程序的完整性。

  1. LC_ENCRYPTION_INFO
  2. LC_CODE_SIGNATURE
  3. <强> 1。 LC_ENCRYPTION_INFO

    首先,LC_ENCRYPTION_INFO存储有关应用程序商店加密的信息&#39;。将应用程序上传到应用商店后,应用会在发布给用户之前进行加密。

    在上传到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
    

    正如你所看到的,&#39; cryptid&#39;上传应用时设置为1。所以检查'cryptid&#39; bit会告诉我们二进制文件是否加密。

    您可能认为只需将该位设置为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 / bin / codesign实际引用的部分。但解析该部分比解析LC_ENCRYPTION_INFO有点困难,因为它没有文档记录,也没有像 signature_info_command 这样的类型。

    LC_CODE_SIGNATURE包含除了部分本身之外的所有二进制文件的哈希值,并且只要重新签名就调整哈希值。

    我移植了/ usr / bin / codesign的代码来解析这一部分。 check here中定义的here SecStaticCode :: validateExecutable

    <强> 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.c

    #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);
    }