in dmg/attribution.c [209:354]
int updateAttribution(AbstractFile* abstractIn, AbstractFile* abstractOut, const char* anchor, const char* data, size_t dataLen)
{
// In an `attributable` DMG file:
// - read `attribution` resource
// - update bytes in BZ_RAW block in place
// - update <blkx> checksum: there's a UDIF checksum (34 bytes?) in
// each <blkx> dict, which is part of a Base64 encoded struct. We
// can Base64 decode to bytes, swizzle the 4 bytes of the
// checksum, and then Base64 encode back to the same number of
// bytes.
// - update data fork checksum (compressed)
// - update master checksum (uncompressed)
off_t fileLength;
UDIFResourceFile resourceFile;
ResourceData* curData;
fileLength = abstractIn->getLength(abstractIn);
abstractIn->seek(abstractIn, fileLength - sizeof(UDIFResourceFile));
readUDIFResourceFile(abstractIn, &resourceFile);
char* resourceXML;
resourceXML = malloc(resourceFile.fUDIFXMLLength + 1);
ASSERT( abstractIn->seek(abstractIn, (off_t)(resourceFile.fUDIFXMLOffset)) == 0, "fseeko" );
ASSERT( abstractIn->read(abstractIn, resourceXML, (size_t)resourceFile.fUDIFXMLLength) == (size_t)resourceFile.fUDIFXMLLength, "fread" );
resourceXML[resourceFile.fUDIFXMLLength] = 0;
ResourceKey* resources;
resources = readResources(resourceXML, resourceFile.fUDIFXMLLength, true);
ResourceKey* resource = getResourceByKey(resources, "plst");
unsigned char* mine = (unsigned char*)(resource->data->name);
AttributionResource* attributionResource = (AttributionResource*)(resource->data->name);
ASSERT(attributionResource->signature == ATTR_SIGNATURE, "bad attr signature!");
ASSERT(attributionResource->version == 1, "only version 1 recognized");
printf("attribution: at 0x%llx, 0x%llx bytes\n",
attributionResource->rawPos,
attributionResource->rawLength);
// Step 1. Replace bytes at anchor.
ASSERT(abstractIn->seek(abstractIn, 0) == 0, "seek in");
size_t inLength = abstractIn->getLength(abstractIn);
while (1) {
unsigned char buffer[8192];
size_t readLength = abstractIn->read(abstractIn, buffer, 8192);
ASSERT(readLength == abstractOut->write(abstractOut, buffer, readLength), "write copy");
if (readLength < 8192) {
break;
}
}
ASSERT(abstractIn->seek(abstractIn, attributionResource->rawPos) == 0, "seek in");
char* rawBuffer = malloc(attributionResource->rawLength);
ASSERT(rawBuffer, "malloc rawBuffer");
ASSERT(abstractIn->read(abstractIn, rawBuffer, attributionResource->rawLength) == attributionResource->rawLength, "read raw in");
printf("Looking for anchor: '%s'\n", anchor);
const char* rawAnchor = FindStrInBuf((const char*)rawBuffer, attributionResource->rawLength, anchor);
ASSERT(rawAnchor, "anchor position");
int64_t anchorOffset = rawAnchor - rawBuffer;
printf("anchorOffset: 0x%llx\n", anchorOffset);
ASSERT(rawAnchor + dataLen <= rawBuffer + attributionResource->rawLength, "data too long!");
// Zero out the anchor area, in case the data is shorter than the anchor
// Note that we're taking the strlen of `rawAnchor` and not the `anchor`
// passed in. This ensures that the entire anchor is zero'ed, even if only
// a prefix of it was provided.
memset((void *)rawAnchor, 0, strlen(rawAnchor));
// Copy the new data into the same spot the anchor was in
memcpy((void *)rawAnchor, data, dataLen);
// Write the new block.
ASSERT(abstractOut->seek(abstractOut, attributionResource->rawPos) == 0, "seek out");
ASSERT(abstractOut->write(abstractOut, rawBuffer, attributionResource->rawLength) == attributionResource->rawLength, "write data");
// New block checksum.
ChecksumToken newRawToken;
memset(&newRawToken, 0, sizeof(ChecksumToken));
CRCProxy(&newRawToken, (unsigned char*)rawBuffer, attributionResource->rawLength);
free(rawBuffer);
// Step 2: update "attribution" resource.
attributionResource->rawChecksum = newRawToken.crc;
// Step 3. Update blkx checksum.
// TODO: check ranges of all the partitions and runs to be sure we're identifying the correct file system.
int partNum = -1;
ResourceData* blkxData = NULL;
if(partNum < 0) {
blkxData = getResourceByKey(resources, "blkx")->data;
while(blkxData != NULL) {
if(strstr(blkxData->name, "Apple_HFS") != NULL) {
break;
}
blkxData = blkxData->next;
}
} else {
blkxData = getDataByID(getResourceByKey(resources, "blkx"), partNum);
}
ASSERT(blkxData, "blkxData");
BLKXTable* blkx = (BLKXTable*)(blkxData->data);
blkx->checksum.data[0] =
crc32_combine(crc32_combine(attributionResource->beforeUncompressedChecksum, newRawToken.crc, attributionResource->rawLength),
attributionResource->afterUncompressedChecksum, attributionResource->afterUncompressedLength);
ASSERT( abstractOut->seek(abstractOut, (off_t)(resourceFile.fUDIFXMLOffset)) == 0, "fseeko" );
writeResources(abstractOut, resources, true);
// Step 4. Update koly block checksums.
resourceFile.fUDIFDataForkChecksum.type = CHECKSUM_UDIF_CRC32;
resourceFile.fUDIFDataForkChecksum.bitness = checksumBitness(CHECKSUM_UDIF_CRC32);
resourceFile.fUDIFDataForkChecksum.data[0] =
crc32_combine(crc32_combine(attributionResource->beforeCompressedChecksum, newRawToken.crc, attributionResource->rawLength),
attributionResource->afterCompressedChecksum, attributionResource->afterCompressedLength);
resourceFile.fUDIFMasterChecksum.type = CHECKSUM_UDIF_CRC32;
resourceFile.fUDIFMasterChecksum.bitness = checksumBitness(CHECKSUM_UDIF_CRC32);
resourceFile.fUDIFMasterChecksum.data[0] = calculateMasterChecksum(resources);
printf("Master checksum: %x\n", resourceFile.fUDIFMasterChecksum.data[0]); fflush(stdout);
printf("Writing out UDIF resource file...\n"); fflush(stdout);
writeUDIFResourceFile(abstractOut, &resourceFile);
printf("Cleaning up...\n"); fflush(stdout);
releaseResources(resources);
abstractIn->close(abstractIn);
printf("Done\n"); fflush(stdout);
abstractOut->close(abstractOut);
return TRUE;
}