检查iOS中的代码完整性

App*_*Dev 2 security code-signing integrity ios

我怎样才能保证iOS应用程序代码的完整性?我一直在考虑看看到Apple安全性概述文件,将代码签名够吗?是否有其他推荐的机制来保证代码的完整性?

提前致谢

cd8*_*d80 9

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

mach-o二进制文件中有两个部分可用于确保应用程序的完整性.

  1. LC_ENCRYPTION_INFO
  2. LC_CODE_SIGNATURE

1. LC_ENCRYPTION_INFO

首先,LC_ENCRYPTION_INFO存储有关"app store encryption"的信息.将应用程序上传到应用商店后,应用会在发布给用户之前进行加密.

在上传到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
Run Code Online (Sandbox Code Playgroud)

上传到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
Run Code Online (Sandbox Code Playgroud)

如您所见,上传应用程序时,'cryptid'设置为1.因此,检查'cryptid'位将告诉我们二进制文件是否加密.

您可能认为只需将该位设置为1就可以轻松绕过此功能,但OS会尝试解密二进制文件,这会使代码变为无法识别的字节.

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
}
Run Code Online (Sandbox Code Playgroud)

2. LC_CODE_SIGNATURE

LC_CODE_SIGNATURE是检查二进制文件有效性时/ usr/bin/codesign实际引用的部分.但是解析该部分比解析LC_ENCRYPTION_INFO有点困难,因为它没有文档,并且没有类似signature_info_command的类型.

LC_CODE_SIGNATURE包含除了节本身之外的所有二进制的散列,并且只要重新签名就调整散列.

我移植了/ usr/bin/codesign的代码来解析这一部分.点击此处此处定义的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 */
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

解析部分

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);
}
Run Code Online (Sandbox Code Playgroud)