VirtualBox

Ignore:
Timestamp:
Oct 9, 2018 9:06:46 PM (6 years ago)
Author:
vboxsync
Message:

IPRT/ldr: More hacking Mach-O code signatures. bugref:9232

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/common/ldr/ldrMachO.cpp

    r74724 r74726  
    6565#include <iprt/mem.h>
    6666#include <iprt/string.h>
     67#include <iprt/sha.h>
     68#include <iprt/crypto/digest.h>
    6769
    6870#include <iprt/formats/mach-o.h>
     
    234236        PCRTCRAPLCSSUPERBLOB    pSuper;
    235237    }                       PtrCodeSignature;
     238    /** File offset of segment 0 (relative to Mach-O header). */
     239    uint64_t                offSeg0ForCodeSign;
     240    /** File size of segment 0. */
     241    uint64_t                cbSeg0ForCodeSign;
     242    /** Segment 0 flags. */
     243    uint64_t                fSeg0ForCodeSign;
    236244
    237245    /** The RVA of the Global Offset Table. */
     
    261269    /** The naturalized size. */
    262270    uint32_t                    cb;
     271    /** The digest type. */
     272    RTDIGESTTYPE                enmDigest;
    263273} RTLDRMACHCODEDIR;
    264274/** Pointer to code directory data. */
     
    11661176    const uint32_t cSegments = pThis->cSegments;
    11671177    PRTLDRMODMACHOSEG pSegItr;
     1178    bool fFirstSeg = true;
    11681179    RT_NOREF(cbStringPool);
    11691180
     
    13151326                    if (fAddSegOuter) \
    13161327                        CLOSE_SEGMENT(); \
    1317                 } while (0) /* ADD_SEGMENT_AND_ITS_SECTIONS */
     1328                    \
     1329                    /* Take down 'execSeg' info for signing */ \
     1330                    if (fFirstSeg) \
     1331                    { \
     1332                        fFirstSeg = false; \
     1333                        pThis->offSeg0ForCodeSign = pSrcSeg->fileoff; \
     1334                        pThis->cbSeg0ForCodeSign  = pSrcSeg->filesize; /** @todo file or vm size? */ \
     1335                        pThis->fSeg0ForCodeSign   = pSrcSeg->flags; \
     1336                    } \
     1337            } while (0) /* ADD_SEGMENT_AND_ITS_SECTIONS */
    13181338
    13191339                ADD_SEGMENT_AND_ITS_SECTIONS(32);
     
    40494069        if (   offData < offFirst
    40504070            || offData > cbBlob - sizeof(RTCRAPLCSHDR)
    4051             || !(offData & 3))
     4071            || (offData & 3))
    40524072            return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
    40534073                                 "Slot #%u has an invalid data offset: %#x (min %#x, max %#x-4)",
     
    40564076
    40574077        /*
    4058          * Code directories.
    4059          */
    4060         if (   pSuper->aSlots[iSlot].uType == RTCRAPLCS_SLOT_CODEDIRECTORY
    4061             || (   pSuper->aSlots[iSlot].uType >= RTCRAPLCS_SLOT_ALTERNATE_CODEDIRECTORIES
    4062                 && pSuper->aSlots[iSlot].uType < RTCRAPLCS_SLOT_ALTERNATE_CODEDIRECTORIES_END))
    4063         {
    4064             if (pSignature->cCodeDirs >= RT_ELEMENTS(pSignature->aCodeDirs))
    4065                 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
    4066                                      "Slot #%u: Too many code directory slots (%u found thus far)",
    4067                                      iSlot, pSignature->cCodeDirs + 1);
    4068             if (   pSuper->aSlots[iSlot].uType == RTCRAPLCS_SLOT_CODEDIRECTORY
    4069                 && pSignature->cCodeDirs > 0)
    4070                 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
    4071                                      "Slot #%u: Already have primary code directory in slot #%u",
    4072                                      iSlot, pSignature->aCodeDirs[0].uSlot);
    4073             if (   pSuper->aSlots[iSlot].uType != RTCRAPLCS_SLOT_CODEDIRECTORY /* lazy bird */
    4074                 && pSignature->cCodeDirs == 0)
    4075                 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
    4076                                      "Slot #%u: Expected alternative code directory after the primary one", iSlot);
    4077             if (cbMaxData < RT_UOFFSETOF(RTCRAPLCSCODEDIRECTORY, uUnused))
    4078                 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
    4079                                      "Slot #%u: Insufficient data vailable for code directory (max %#x)", iSlot, cbMaxData);
    4080 
    4081             PCRTCRAPLCSCODEDIRECTORY pCodeDir = (PCRTCRAPLCSCODEDIRECTORY)&pThis->PtrCodeSignature.pb[offData];
    4082             if (pCodeDir->Hdr.uMagic != RTCRAPLCS_MAGIC_CODEDIRECTORY)
    4083                 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
    4084                                      "Slot #%u: Invalid code directory magic: %#x", iSlot, RT_BE2H_U32(pCodeDir->Hdr.uMagic));
    4085             uint32_t const cb = RT_BE2H_U32(pCodeDir->Hdr.cb);
    4086             if (cb > cbMaxData || cb < RT_UOFFSETOF(RTCRAPLCSCODEDIRECTORY, offScatter))
    4087                 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
    4088                                      "Slot #%u: Code directory size is out of bound: %#x (min %#x, max %#x)",
    4089                                      iSlot, cb, RT_UOFFSETOF(RTCRAPLCSCODEDIRECTORY, offScatter), cbMaxData);
    4090 /** @todo validate all the fields we wish to use here. */
    4091 
    4092             pSignature->aCodeDirs[pSignature->cCodeDirs].pCodeDir = pCodeDir;
    4093             pSignature->aCodeDirs[pSignature->cCodeDirs].uSlot    = iSlot;
    4094             pSignature->aCodeDirs[pSignature->cCodeDirs].cb       = cb;
    4095             pSignature->cCodeDirs++;
    4096         }
    4097         /*
    40984078         * PKCS#7/CMS signature.
    40994079         */
    4100         else if (pSuper->aSlots[iSlot].uType == RTCRAPLCS_SLOT_SIGNATURE)
     4080        if (pSuper->aSlots[iSlot].uType == RTCRAPLCS_SLOT_SIGNATURE)
    41014081        {
    41024082            if (pSignature->idxPkcs7 != UINT32_MAX)
     
    41154095            pSignature->pbPkcs7  = (uint8_t const *)(pHdr + 1);
    41164096            pSignature->cbPkcs7  = cb - sizeof(*pHdr);
     4097        }
     4098        /*
     4099         * Code directories.
     4100         */
     4101        else if (   pSuper->aSlots[iSlot].uType == RTCRAPLCS_SLOT_CODEDIRECTORY
     4102                 || (  RT_BE2H_U32(pSuper->aSlots[iSlot].uType) - RT_BE2H_U32_C(RTCRAPLCS_SLOT_ALTERNATE_CODEDIRECTORIES)
     4103                     < RTCRAPLCS_SLOT_ALTERNATE_CODEDIRECTORIES_COUNT))
     4104        {
     4105            /* Make sure we don't get too many code directories and that the first one is a regular one. */
     4106            if (pSignature->cCodeDirs >= RT_ELEMENTS(pSignature->aCodeDirs))
     4107                return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
     4108                                     "Slot #%u: Too many code directory slots (%u found thus far)",
     4109                                     iSlot, pSignature->cCodeDirs + 1);
     4110            if (   pSuper->aSlots[iSlot].uType == RTCRAPLCS_SLOT_CODEDIRECTORY
     4111                && pSignature->cCodeDirs > 0)
     4112                return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
     4113                                     "Slot #%u: Already have primary code directory in slot #%u",
     4114                                     iSlot, pSignature->aCodeDirs[0].uSlot);
     4115            if (   pSuper->aSlots[iSlot].uType != RTCRAPLCS_SLOT_CODEDIRECTORY /* lazy bird */
     4116                && pSignature->cCodeDirs == 0)
     4117                return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
     4118                                     "Slot #%u: Expected alternative code directory after the primary one", iSlot);
     4119
     4120            /* Check data header: */
     4121            if (cbMaxData < RT_UOFFSETOF(RTCRAPLCSCODEDIRECTORY, uUnused1))
     4122                return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
     4123                                     "Slot #%u: Insufficient data vailable for code directory (max %#x)", iSlot, cbMaxData);
     4124
     4125            PCRTCRAPLCSCODEDIRECTORY pCodeDir = (PCRTCRAPLCSCODEDIRECTORY)&pThis->PtrCodeSignature.pb[offData];
     4126            if (pCodeDir->Hdr.uMagic != RTCRAPLCS_MAGIC_CODEDIRECTORY)
     4127                return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
     4128                                     "Slot #%u: Invalid code directory magic: %#x", iSlot, RT_BE2H_U32(pCodeDir->Hdr.uMagic));
     4129            uint32_t const cbCodeDir = RT_BE2H_U32(pCodeDir->Hdr.cb);
     4130            if (cbCodeDir > cbMaxData || cbCodeDir < RT_UOFFSETOF(RTCRAPLCSCODEDIRECTORY, offScatter))
     4131                return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
     4132                                     "Slot #%u: Code directory size is out of bound: %#x (min %#x, max %#x)",
     4133                                     iSlot, cbCodeDir, RT_UOFFSETOF(RTCRAPLCSCODEDIRECTORY, offScatter), cbMaxData);
     4134            pSignature->aCodeDirs[pSignature->cCodeDirs].pCodeDir = pCodeDir;
     4135            pSignature->aCodeDirs[pSignature->cCodeDirs].cb       = cbCodeDir;
     4136
     4137            /* Check Version: */
     4138            uint32_t const uVersion = RT_BE2H_U32(pCodeDir->uVersion);
     4139            if (   uVersion < RTCRAPLCS_VER_2_0
     4140                || uVersion >= RT_MAKE_U32(0, 3))
     4141                return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
     4142                                     "Slot #%u: Code directory version is out of bounds: %#07x", iSlot, uVersion);
     4143            uint32_t cbSelf = uVersion >= RTCRAPLCS_VER_SUPPORTS_EXEC_SEG      ? RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, fExecSeg)
     4144                            : uVersion >= RTCRAPLCS_VER_SUPPORTS_CODE_LIMIT_64 ? RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, cbCodeLimit64)
     4145                            : uVersion >= RTCRAPLCS_VER_SUPPORTS_TEAMID        ? RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, offTeamId)
     4146                            : uVersion >= RTCRAPLCS_VER_SUPPORTS_SCATTER       ? RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, offScatter)
     4147                            :                                                    RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, uUnused1);
     4148            if (cbSelf > cbCodeDir)
     4149                return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
     4150                                     "Slot #%u: Code directory size is out of bound: %#x (min %#x, max %#x)",
     4151                                     iSlot, cbCodeDir, cbSelf, cbCodeDir);
     4152
     4153            /* hash type and size. */
     4154            uint8_t      cbHash;
     4155            RTDIGESTTYPE enmDigest;
     4156            switch (pCodeDir->bHashType)
     4157            {
     4158                case RTCRAPLCS_HASHTYPE_SHA1:
     4159                    enmDigest = RTDIGESTTYPE_SHA1;
     4160                    cbHash    = RTSHA1_HASH_SIZE;
     4161                    break;
     4162                case RTCRAPLCS_HASHTYPE_SHA256:
     4163                    enmDigest = RTDIGESTTYPE_SHA256;
     4164                    cbHash    = RTSHA256_HASH_SIZE;
     4165                    break;
     4166                case RTCRAPLCS_HASHTYPE_SHA256_TRUNCATED:
     4167                    enmDigest = RTDIGESTTYPE_SHA256;
     4168                    cbHash    = RTSHA1_HASH_SIZE; /* truncated to SHA-1 size. */
     4169                    break;
     4170                case RTCRAPLCS_HASHTYPE_SHA384:
     4171                    enmDigest = RTDIGESTTYPE_SHA384;
     4172                    cbHash    = RTSHA384_HASH_SIZE;
     4173                    break;
     4174                default:
     4175                    return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "Slot #%u: Unknown hash type %#x (LB %#x)",
     4176                                         iSlot, pCodeDir->bHashType, pCodeDir->cbHash);
     4177            }
     4178            pSignature->aCodeDirs[pSignature->cCodeDirs].enmDigest = enmDigest;
     4179            if (pCodeDir->cbHash != cbHash)
     4180                return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
     4181                                     "Slot #%u: Unexpected hash size for %s: %#x, expected %#x",
     4182                                     iSlot, RTCrDigestTypeToName(enmDigest), pCodeDir->cbHash, cbHash);
     4183
     4184            /* Hash slot offset and counts. Special slots are counted backwards from offHashSlots. */
     4185            uint32_t const cSpecialSlots = RT_BE2H_U32(pCodeDir->cSpecialSlots);
     4186            if (cSpecialSlots > 256)
     4187                return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
     4188                                     "Slot #%u: Too many special slots: %#x", iSlot, cSpecialSlots);
     4189            uint32_t const cCodeSlots = RT_BE2H_U32(pCodeDir->cCodeSlots);
     4190            if (   cCodeSlots >= UINT32_MAX / 2
     4191                || cCodeSlots + cSpecialSlots > (cbCodeDir - cbHash) / cbHash)
     4192                return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "Slot #%u: Too many code slots: %#x + %#x (max %#x)",
     4193                                     iSlot, cCodeSlots, cSpecialSlots, (cbCodeDir - cbHash) / cbHash);
     4194            uint32_t const offHashSlots = RT_BE2H_U32(pCodeDir->offHashSlots);
     4195            if (   offHashSlots > cbCodeDir - cCodeSlots * cbHash
     4196                || offHashSlots < cbSelf + cSpecialSlots * cbHash)
     4197                return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
     4198                                     "Slot #%u: Code directory hash offset is out of bounds: %#x (min: %#x, max: %#x)",
     4199                                     iSlot, offHashSlots, cbSelf + cSpecialSlots * cbHash, cbCodeDir - cCodeSlots * cbHash);
     4200
     4201            /* page shift */
     4202            if (pCodeDir->cPageShift == 0)
     4203                return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
     4204                                     "Slot #%u: Unsupported page shift of zero in code directory", iSlot);
     4205            uint32_t cMaxPageShift;
     4206            if (   pThis->Core.enmArch == RTLDRARCH_AMD64
     4207                || pThis->Core.enmArch == RTLDRARCH_X86_32
     4208                || pThis->Core.enmArch == RTLDRARCH_ARM32)
     4209                cMaxPageShift = 12;
     4210            else if (pThis->Core.enmArch == RTLDRARCH_ARM64)
     4211                cMaxPageShift = 16; /* 16KB */
     4212            else
     4213                return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "Unsupported architecture: %d", pThis->Core.enmArch);
     4214            if (   pCodeDir->cPageShift < 12 /* */
     4215                || pCodeDir->cPageShift > cMaxPageShift)
     4216                return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
     4217                                     "Slot #%u: Page shift in code directory is out of range: %d (min: 12, max: %d)",
     4218                                     iSlot, pCodeDir->cPageShift, cMaxPageShift);
     4219
     4220            /* code limit vs page shift and code hash slots */
     4221            uint32_t const cbCodeLimit32       = RT_BE2H_U32(pCodeDir->cbCodeLimit32);
     4222            uint32_t const cExpectedCodeHashes = pCodeDir->cPageShift == 0 ? 1
     4223                                               : (cbCodeLimit32 + RT_BIT_32(pCodeDir->cPageShift) - 1) >> pCodeDir->cPageShift;
     4224            if (cExpectedCodeHashes != cCodeSlots)
     4225                return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
     4226                                     "Slot #%u: Code limit and page shift value does not match code hash slots: cbCodeLimit32=%#x cPageShift=%u -> %#x; cCodeSlots=%#x",
     4227                                     iSlot, cbCodeLimit32, pCodeDir->cPageShift, cExpectedCodeHashes, cCodeSlots);
     4228
     4229            /* Identifier offset: */
     4230            if (pCodeDir->offIdentifier)
     4231            {
     4232                uint32_t const offIdentifier = RT_BE2H_U32(pCodeDir->offIdentifier);
     4233                if (   offIdentifier < cbSelf
     4234                    || offIdentifier >= cbCodeDir)
     4235                    return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
     4236                                         "Slot #%u: Identifier offset is out of bounds: %#x (min: %#x, max: %#x)",
     4237                                         iSlot, offIdentifier, cbSelf, cbCodeDir - 1);
     4238                int rc = RTStrValidateEncodingEx((char const *)pCodeDir + offIdentifier, cbCodeDir - offIdentifier,
     4239                                                 RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
     4240                if (RT_FAILURE(rc))
     4241                    return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
     4242                                         "Slot #%u: Malformed identifier string: %Rrc", iSlot, rc);
     4243            }
     4244
     4245            /* Team identifier: */
     4246            if (cbSelf >= RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, offTeamId) && pCodeDir->offTeamId)
     4247            {
     4248                uint32_t const offTeamId = RT_BE2H_U32(pCodeDir->offTeamId);
     4249                if (   offTeamId < cbSelf
     4250                    || offTeamId >= cbCodeDir)
     4251                    return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
     4252                                         "Slot #%u: Team identifier offset is out of bounds: %#x (min: %#x, max: %#x)",
     4253                                         iSlot, offTeamId, cbSelf, cbCodeDir - 1);
     4254                int rc = RTStrValidateEncodingEx((char const *)pCodeDir + offTeamId, cbCodeDir - offTeamId,
     4255                                                 RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
     4256                if (RT_FAILURE(rc))
     4257                    return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
     4258                                         "Slot #%u: Malformed team identifier string: %Rrc", iSlot, rc);
     4259            }
     4260
     4261            /* We don't support scatter. */
     4262            if (cbSelf >= RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, offScatter) && pCodeDir->offScatter)
     4263                return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
     4264                                     "Slot #%u: Scatter not supported.", iSlot);
     4265
     4266            /* We don't really support the 64-bit code limit either: */
     4267            if (   cbSelf >= RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, cbCodeLimit64)
     4268                && pCodeDir->cbCodeLimit64
     4269                && RT_BE2H_U64(pCodeDir->cbCodeLimit64) != cbCodeLimit32)
     4270                return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
     4271                                     "Slot #%u: 64-bit code limit does not match 32-bit: %#RX64 vs %#RX32",
     4272                                     iSlot, RT_BE2H_U64(pCodeDir->cbCodeLimit64), cbCodeLimit32);
     4273
     4274            /* Check executable segment info if present: */
     4275            if (   cbSelf >= RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, fExecSeg)
     4276                && (   pThis->offSeg0ForCodeSign != RT_BE2H_U64(pCodeDir->offExecSeg)
     4277                    || pThis->cbSeg0ForCodeSign != RT_BE2H_U64(pCodeDir->cbExecSeg)
     4278                    || pThis->fSeg0ForCodeSign != RT_BE2H_U64(pCodeDir->fFlags)) )
     4279                return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
     4280                                     "Slot #%u: Segment #0 info mismatch: @%#RX64 LB %#RX64 flags=%#RX64; expected @%#RX64 LB %#RX64 flags=%#RX64",
     4281                                     iSlot, RT_BE2H_U64(pCodeDir->offExecSeg), RT_BE2H_U64(pCodeDir->cbExecSeg),
     4282                                     RT_BE2H_U64(pCodeDir->fExecSeg), pThis->offSeg0ForCodeSign, pThis->cbSeg0ForCodeSign,
     4283                                     pThis->fSeg0ForCodeSign);
     4284
     4285            /* Check fields that must be zero (don't want anyone to use them to counter changes): */
     4286            if (pCodeDir->uUnused1 != 0)
     4287                return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
     4288                                     "Slot #%u: Unused field #1 is non-zero: %#x", iSlot, RT_BE2H_U32(pCodeDir->uUnused1));
     4289            if (   cbSelf >= RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, uUnused2)
     4290                && pCodeDir->uUnused2 != 0)
     4291                return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
     4292                                     "Slot #%u: Unused field #2 is non-zero: %#x", iSlot, RT_BE2H_U32(pCodeDir->uUnused2));
     4293
     4294            /** @todo idPlatform values.   */
     4295
     4296
     4297            /* Commit the code dir entry: */
     4298            pSignature->aCodeDirs[pSignature->cCodeDirs++].uSlot = iSlot;
    41174299        }
    41184300    }
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette