diff --git a/ZSign/Makefile b/ZSign/Makefile new file mode 100644 index 0000000..605f331 --- /dev/null +++ b/ZSign/Makefile @@ -0,0 +1,14 @@ +TARGET := iphone:clang:latest:7.0 +ARCHS := arm64 +include $(THEOS)/makefiles/common.mk + +LIBRARY_NAME = ZSign + +ZSign_FILES = $(shell find . -name '*.cpp') $(shell find . -name '*.mm') zsigner.m +ZSign_CFLAGS = -fobjc-arc -Wno-deprecated -Wno-unused-variable -Wno-unused-but-set-variable -Wno-module-import-in-extern-c +ZSign_CCFLAGS = -std=c++11 +ZSign_FRAMEWORKS = OpenSSL +ZSign_INSTALL_PATH = /Applications/LiveContainer.app/Frameworks + +include $(THEOS_MAKE_PATH)/library.mk + diff --git a/ZSign/Utils.hpp b/ZSign/Utils.hpp new file mode 100644 index 0000000..d451634 --- /dev/null +++ b/ZSign/Utils.hpp @@ -0,0 +1,26 @@ +// +// Utils.hpp +// feather +// +// Created by samara on 30.09.2024. +// + +#ifndef Utils_hpp +#define Utils_hpp + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +const char* getDocumentsDirectory(); +void writeToNSLog(const char* msg); +void refreshFile(const char* path); + +#ifdef __cplusplus +} +#endif + +#endif /* zsign_hpp */ diff --git a/ZSign/Utils.mm b/ZSign/Utils.mm new file mode 100644 index 0000000..332a2cc --- /dev/null +++ b/ZSign/Utils.mm @@ -0,0 +1,38 @@ +// +// Utils.cpp +// feather +// +// Created by samara on 30.09.2024. +// + +#include "Utils.hpp" +#import + +extern "C" { + +const char* getDocumentsDirectory() { + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths firstObject]; + const char *documentsPath = [documentsDirectory UTF8String]; + return documentsPath; +} + +void writeToNSLog(const char* msg) { + NSLog(@"[LC] singner msg: %s", msg); +} + +// copy, remove and rename back the file to prevent crash due to kernel signature cache +// see https://developer.apple.com/documentation/security/updating-mac-software +void refreshFile(const char* path) { + NSString* objcPath = @(path); + if(![NSFileManager.defaultManager fileExistsAtPath:objcPath]) { + return; + } + NSString* newPath = [NSString stringWithFormat:@"%s.tmp", path]; + NSError* error; + [NSFileManager.defaultManager copyItemAtPath:objcPath toPath:newPath error:&error]; + [NSFileManager.defaultManager removeItemAtPath:objcPath error:&error]; + [NSFileManager.defaultManager moveItemAtPath:newPath toPath:objcPath error:&error]; +} + +} diff --git a/ZSign/archo.cpp b/ZSign/archo.cpp new file mode 100644 index 0000000..f3de7d4 --- /dev/null +++ b/ZSign/archo.cpp @@ -0,0 +1,860 @@ +#include "common/common.h" +#include "common/json.h" +#include "archo.h" +#include "signing.h" + +static uint64_t execSegLimit = 0; + +ZArchO::ZArchO() +{ + m_pBase = NULL; + m_uLength = 0; + m_uCodeLength = 0; + m_pSignBase = NULL; + m_uSignLength = 0; + m_pHeader = NULL; + m_uHeaderSize = 0; + m_bEncrypted = false; + m_b64 = false; + m_bBigEndian = false; + m_bEnoughSpace = true; + m_pCodeSignSegment = NULL; + m_pLinkEditSegment = NULL; + m_uLoadCommandsFreeSpace = 0; +} + +bool ZArchO::Init(uint8_t *pBase, uint32_t uLength) +{ + if (NULL == pBase || uLength <= 0) + { + return false; + } + + m_pBase = pBase; + m_uLength = uLength; + m_uCodeLength = (uLength % 16 == 0) ? uLength : uLength + 16 - (uLength % 16); + m_pHeader = (mach_header *)m_pBase; + if (MH_MAGIC != m_pHeader->magic && MH_CIGAM != m_pHeader->magic && MH_MAGIC_64 != m_pHeader->magic && MH_CIGAM_64 != m_pHeader->magic) + { + return false; + } + + m_b64 = (MH_MAGIC_64 == m_pHeader->magic || MH_CIGAM_64 == m_pHeader->magic) ? true : false; + m_bBigEndian = (MH_CIGAM == m_pHeader->magic || MH_CIGAM_64 == m_pHeader->magic) ? true : false; + m_uHeaderSize = m_b64 ? sizeof(mach_header_64) : sizeof(mach_header); + + uint8_t *pLoadCommand = m_pBase + m_uHeaderSize; + for (uint32_t i = 0; i < BO(m_pHeader->ncmds); i++) + { + load_command *plc = (load_command *)pLoadCommand; + switch (BO(plc->cmd)) + { + case LC_SEGMENT: + { + segment_command *seglc = (segment_command *)pLoadCommand; + if (0 == strcmp("__TEXT", seglc->segname)) + { + execSegLimit = seglc->vmsize; + for (uint32_t j = 0; j < BO(seglc->nsects); j++) + { + section *sect = (section *)((pLoadCommand + sizeof(segment_command)) + sizeof(section) * j); + if (0 == strcmp("__text", sect->sectname)) + { + if (BO(sect->offset) > (BO(m_pHeader->sizeofcmds) + m_uHeaderSize)) + { + m_uLoadCommandsFreeSpace = BO(sect->offset) - BO(m_pHeader->sizeofcmds) - m_uHeaderSize; + } + } + else if (0 == strcmp("__info_plist", sect->sectname)) + { + m_strInfoPlist.append((const char *)m_pBase + BO(sect->offset), BO(sect->size)); + } + } + } + else if (0 == strcmp("__LINKEDIT", seglc->segname)) + { + m_pLinkEditSegment = pLoadCommand; + } + } + break; + case LC_SEGMENT_64: + { + segment_command_64 *seglc = (segment_command_64 *)pLoadCommand; + if (0 == strcmp("__TEXT", seglc->segname)) + { + execSegLimit = seglc->vmsize; + for (uint32_t j = 0; j < BO(seglc->nsects); j++) + { + section_64 *sect = (section_64 *)((pLoadCommand + sizeof(segment_command_64)) + sizeof(section_64) * j); + if (0 == strcmp("__text", sect->sectname)) + { + if (BO(sect->offset) > (BO(m_pHeader->sizeofcmds) + m_uHeaderSize)) + { + m_uLoadCommandsFreeSpace = BO(sect->offset) - BO(m_pHeader->sizeofcmds) - m_uHeaderSize; + } + } + else if (0 == strcmp("__info_plist", sect->sectname)) + { + m_strInfoPlist.append((const char *)m_pBase + BO(sect->offset), BO((uint32_t)sect->size)); + } + } + } + else if (0 == strcmp("__LINKEDIT", seglc->segname)) + { + m_pLinkEditSegment = pLoadCommand; + } + } + break; + case LC_ENCRYPTION_INFO: + case LC_ENCRYPTION_INFO_64: + { + encryption_info_command *crypt_cmd = (encryption_info_command *)pLoadCommand; + if (BO(crypt_cmd->cryptid) >= 1) + { + m_bEncrypted = true; + } + } + break; + case LC_CODE_SIGNATURE: + { + codesignature_command *pcslc = (codesignature_command *)pLoadCommand; + m_pCodeSignSegment = pLoadCommand; + m_uCodeLength = BO(pcslc->dataoff); + m_pSignBase = m_pBase + m_uCodeLength; + m_uSignLength = GetCodeSignatureLength(m_pSignBase); + } + break; + } + + pLoadCommand += BO(plc->cmdsize); + } + + return true; +} + +const char *ZArchO::GetArch(int cpuType, int cpuSubType) +{ + switch (cpuType) + { + case CPU_TYPE_ARM: + { + switch (cpuSubType) + { + case CPU_SUBTYPE_ARM_V6: + return "armv6"; + break; + case CPU_SUBTYPE_ARM_V7: + return "armv7"; + break; + case CPU_SUBTYPE_ARM_V7S: + return "armv7s"; + break; + case CPU_SUBTYPE_ARM_V7K: + return "armv7k"; + break; + case CPU_SUBTYPE_ARM_V8: + return "armv8"; + break; + } + } + break; + case CPU_TYPE_ARM64: + { + switch (cpuSubType) + { + case CPU_SUBTYPE_ARM64_ALL: + return "arm64"; + break; + case CPU_SUBTYPE_ARM64_V8: + return "arm64v8"; + break; + case 2: + return "arm64e"; + break; + } + } + break; + case CPU_TYPE_ARM64_32: + { + switch (cpuSubType) + { + case CPU_SUBTYPE_ARM64_ALL: + return "arm64_32"; + break; + case CPU_SUBTYPE_ARM64_32_V8: + return "arm64e_32"; + break; + } + } + break; + case CPU_TYPE_X86: + { + switch (cpuSubType) + { + default: + return "x86_32"; + break; + } + } + break; + case CPU_TYPE_X86_64: + { + switch (cpuSubType) + { + default: + return "x86_64"; + break; + } + } + break; + } + return "unknown"; +} + +const char *ZArchO::GetFileType(uint32_t uFileType) +{ + switch (uFileType) + { + case MH_OBJECT: + return "MH_OBJECT"; + break; + case MH_EXECUTE: + return "MH_EXECUTE"; + break; + case MH_FVMLIB: + return "MH_FVMLIB"; + break; + case MH_CORE: + return "MH_CORE"; + break; + case MH_PRELOAD: + return "MH_PRELOAD"; + break; + case MH_DYLIB: + return "MH_DYLIB"; + break; + case MH_DYLINKER: + return "MH_DYLINKER"; + break; + case MH_BUNDLE: + return "MH_BUNDLE"; + break; + case MH_DYLIB_STUB: + return "MH_DYLIB_STUB"; + break; + case MH_DSYM: + return "MH_DSYM"; + break; + case MH_KEXT_BUNDLE: + return "MH_KEXT_BUNDLE"; + break; + } + return "MH_UNKNOWN"; +} + +uint32_t ZArchO::BO(uint32_t uValue) +{ + return m_bBigEndian ? LE(uValue) : uValue; +} + +bool ZArchO::IsExecute() +{ + if (NULL != m_pHeader) + { + return (MH_EXECUTE == BO(m_pHeader->filetype)); + } + return false; +} + +void ZArchO::PrintInfo() +{ + if (NULL == m_pHeader) + { + return; + } + + ZLog::Print("------------------------------------------------------------------\n"); + ZLog::Print(">>> MachO Info: \n"); + ZLog::PrintV("\tFileType: \t%s\n", GetFileType(BO(m_pHeader->filetype))); + ZLog::PrintV("\tTotalSize: \t%u (%s)\n", m_uLength, FormatSize(m_uLength).c_str()); + ZLog::PrintV("\tPlatform: \t%u\n", m_b64 ? 64 : 32); + ZLog::PrintV("\tCPUArch: \t%s\n", GetArch(BO(m_pHeader->cputype), BO(m_pHeader->cpusubtype))); + ZLog::PrintV("\tCPUType: \t0x%x\n", BO(m_pHeader->cputype)); + ZLog::PrintV("\tCPUSubType: \t0x%x\n", BO(m_pHeader->cpusubtype)); + ZLog::PrintV("\tBigEndian: \t%d\n", m_bBigEndian); + ZLog::PrintV("\tEncrypted: \t%d\n", m_bEncrypted); + ZLog::PrintV("\tCommandCount: \t%d\n", BO(m_pHeader->ncmds)); + ZLog::PrintV("\tCodeLength: \t%d (%s)\n", m_uCodeLength, FormatSize(m_uCodeLength).c_str()); + ZLog::PrintV("\tSignLength: \t%d (%s)\n", m_uSignLength, FormatSize(m_uSignLength).c_str()); + ZLog::PrintV("\tSpareLength: \t%d (%s)\n", m_uLength - m_uCodeLength - m_uSignLength, FormatSize(m_uLength - m_uCodeLength - m_uSignLength).c_str()); + + uint8_t *pLoadCommand = m_pBase + m_uHeaderSize; + for (uint32_t i = 0; i < BO(m_pHeader->ncmds); i++) + { + load_command *plc = (load_command *)pLoadCommand; + if (LC_VERSION_MIN_IPHONEOS == BO(plc->cmd)) + { + ZLog::PrintV("\tMIN_IPHONEOS: \t0x%x\n", *((uint32_t *)(pLoadCommand + sizeof(load_command)))); + } + else if (LC_RPATH == BO(plc->cmd)) + { + ZLog::PrintV("\tLC_RPATH: \t%s\n", (char *)(pLoadCommand + sizeof(load_command) + 4)); + } + pLoadCommand += BO(plc->cmdsize); + } + + bool bHasWeakDylib = false; + ZLog::PrintV("\tLC_LOAD_DYLIB: \n"); + pLoadCommand = m_pBase + m_uHeaderSize; + for (uint32_t i = 0; i < BO(m_pHeader->ncmds); i++) + { + load_command *plc = (load_command *)pLoadCommand; + if (LC_LOAD_DYLIB == BO(plc->cmd)) + { + dylib_command *dlc = (dylib_command *)pLoadCommand; + const char *szDyLib = (const char *)(pLoadCommand + BO(dlc->dylib.name.offset)); + ZLog::PrintV("\t\t\t%s\n", szDyLib); + } + else if (LC_LOAD_WEAK_DYLIB == BO(plc->cmd)) + { + bHasWeakDylib = true; + } + pLoadCommand += BO(plc->cmdsize); + } + + if (bHasWeakDylib) + { + ZLog::PrintV("\tLC_LOAD_WEAK_DYLIB: \n"); + pLoadCommand = m_pBase + m_uHeaderSize; + for (uint32_t i = 0; i < BO(m_pHeader->ncmds); i++) + { + load_command *plc = (load_command *)pLoadCommand; + if (LC_LOAD_WEAK_DYLIB == BO(plc->cmd)) + { + dylib_command *dlc = (dylib_command *)pLoadCommand; + const char *szDyLib = (const char *)(pLoadCommand + BO(dlc->dylib.name.offset)); + ZLog::PrintV("\t\t\t%s (weak)\n", szDyLib); + } + pLoadCommand += BO(plc->cmdsize); + } + } + + if (!m_strInfoPlist.empty()) + { + ZLog::Print("\n>>> Embedded Info.plist: \n"); + ZLog::PrintV("\tlength: \t%lu\n", m_strInfoPlist.size()); + + string strInfoPlist = m_strInfoPlist; + PWriter::StringReplace(strInfoPlist, "\n", "\n\t\t\t"); + ZLog::PrintV("\tcontent: \t%s\n", strInfoPlist.c_str()); + + PrintDataSHASum("\tSHA-1: \t", E_SHASUM_TYPE_1, m_strInfoPlist); + PrintDataSHASum("\tSHA-256:\t", E_SHASUM_TYPE_256, m_strInfoPlist); + } + + if (NULL == m_pSignBase || m_uSignLength <= 0) + { + ZLog::Warn(">>> Can't Find CodeSignature Segment!\n"); + } + else + { + ParseCodeSignature(m_pSignBase); + } + + ZLog::Print("------------------------------------------------------------------\n"); +} + +bool ZArchO::BuildCodeSignature(ZSignAsset *pSignAsset, bool bForce, const string &strBundleId, const string &strInfoPlistSHA1, const string &strInfoPlistSHA256, const string &strCodeResourcesSHA1, const string &strCodeResourcesSHA256, string &strOutput) +{ + string strRequirementsSlot; + string strEntitlementsSlot; + string strDerEntitlementsSlot; + + // modified, we don't need Entitlement in LiveContainer. +// string strEmptyEntitlements = "\n\n\n\n\n"; + string strEmptyEntitlements = ""; + SlotBuildRequirements(strBundleId, pSignAsset->m_strSubjectCN, strRequirementsSlot); + SlotBuildEntitlements(IsExecute() ? pSignAsset->m_strEntitlementsData : strEmptyEntitlements, strEntitlementsSlot); + SlotBuildDerEntitlements(IsExecute() ? pSignAsset->m_strEntitlementsData : "", strDerEntitlementsSlot); + + string strRequirementsSlotSHA1; + string strRequirementsSlotSHA256; + if (strRequirementsSlot.empty()) + { //empty + strRequirementsSlotSHA1.append(20, 0); + strRequirementsSlotSHA256.append(32, 0); + } + else + { + SHASum(strRequirementsSlot, strRequirementsSlotSHA1, strRequirementsSlotSHA256); + } + + string strEntitlementsSlotSHA1; + string strEntitlementsSlotSHA256; + if (strEntitlementsSlot.empty()) + { //empty + strEntitlementsSlotSHA1.append(20, 0); + strEntitlementsSlotSHA256.append(32, 0); + } + else + { + SHASum(strEntitlementsSlot, strEntitlementsSlotSHA1, strEntitlementsSlotSHA256); + } + + string strDerEntitlementsSlotSHA1; + string strDerEntitlementsSlotSHA256; + if (strDerEntitlementsSlot.empty()) + { //empty + strDerEntitlementsSlotSHA1.append(20, 0); + strDerEntitlementsSlotSHA256.append(32, 0); + } + else + { + SHASum(strDerEntitlementsSlot, strDerEntitlementsSlotSHA1, strDerEntitlementsSlotSHA256); + } + + uint8_t *pCodeSlots1Data = NULL; + uint8_t *pCodeSlots256Data = NULL; + uint32_t uCodeSlots1DataLength = 0; + uint32_t uCodeSlots256DataLength = 0; + if (!bForce) + { + GetCodeSignatureExistsCodeSlotsData(m_pSignBase, pCodeSlots1Data, uCodeSlots1DataLength, pCodeSlots256Data, uCodeSlots256DataLength); + } + + uint64_t execSegFlags = 0; + if (NULL != strstr(strEntitlementsSlot.data() + 8, "get-task-allow")) + { + // TODO: Check if get-task-allow is actually set to true + execSegFlags = CS_EXECSEG_MAIN_BINARY | CS_EXECSEG_ALLOW_UNSIGNED; + } + + string strCMSSignatureSlot; + string strCodeDirectorySlot; + string strAltnateCodeDirectorySlot; + SlotBuildCodeDirectory(false, + m_pBase, + m_uCodeLength, + pCodeSlots1Data, + uCodeSlots1DataLength, + execSegLimit, + execSegFlags, + strBundleId, + pSignAsset->m_strTeamId, + strInfoPlistSHA1, + strRequirementsSlotSHA1, + strCodeResourcesSHA1, + strEntitlementsSlotSHA1, + strDerEntitlementsSlotSHA1, + IsExecute(), + strCodeDirectorySlot); + SlotBuildCodeDirectory(true, + m_pBase, + m_uCodeLength, + pCodeSlots256Data, + uCodeSlots256DataLength, + execSegLimit, + execSegFlags, + strBundleId, + pSignAsset->m_strTeamId, + strInfoPlistSHA256, + strRequirementsSlotSHA256, + strCodeResourcesSHA256, + strEntitlementsSlotSHA256, + strDerEntitlementsSlotSHA256, + IsExecute(), + strAltnateCodeDirectorySlot); + SlotBuildCMSSignature(pSignAsset, + strCodeDirectorySlot, + strAltnateCodeDirectorySlot, + strCMSSignatureSlot); + + uint32_t uCodeDirectorySlotLength = (uint32_t)strCodeDirectorySlot.size(); + uint32_t uRequirementsSlotLength = (uint32_t)strRequirementsSlot.size(); + uint32_t uEntitlementsSlotLength = (uint32_t)strEntitlementsSlot.size(); + uint32_t uDerEntitlementsLength = (uint32_t)strDerEntitlementsSlot.size(); + uint32_t uAltnateCodeDirectorySlotLength = (uint32_t)strAltnateCodeDirectorySlot.size(); + uint32_t uCMSSignatureSlotLength = (uint32_t)strCMSSignatureSlot.size(); + + uint32_t uCodeSignBlobCount = 0; + uCodeSignBlobCount += (uCodeDirectorySlotLength > 0) ? 1 : 0; + uCodeSignBlobCount += (uRequirementsSlotLength > 0) ? 1 : 0; + uCodeSignBlobCount += (uEntitlementsSlotLength > 0) ? 1 : 0; + uCodeSignBlobCount += (uDerEntitlementsLength > 0) ? 1 : 0; + uCodeSignBlobCount += (uAltnateCodeDirectorySlotLength > 0) ? 1 : 0; + uCodeSignBlobCount += (uCMSSignatureSlotLength > 0) ? 1 : 0; + + uint32_t uSuperBlobHeaderLength = sizeof(CS_SuperBlob) + uCodeSignBlobCount * sizeof(CS_BlobIndex); + uint32_t uCodeSignLength = uSuperBlobHeaderLength + + uCodeDirectorySlotLength + + uRequirementsSlotLength + + uEntitlementsSlotLength + + uDerEntitlementsLength + + uAltnateCodeDirectorySlotLength + + uCMSSignatureSlotLength; + + vector arrBlobIndexes; + if (uCodeDirectorySlotLength > 0) + { + CS_BlobIndex blob; + blob.type = BE(CSSLOT_CODEDIRECTORY); + blob.offset = BE(uSuperBlobHeaderLength); + arrBlobIndexes.push_back(blob); + } + if (uRequirementsSlotLength > 0) + { + CS_BlobIndex blob; + blob.type = BE(CSSLOT_REQUIREMENTS); + blob.offset = BE(uSuperBlobHeaderLength + uCodeDirectorySlotLength); + arrBlobIndexes.push_back(blob); + } + if (uEntitlementsSlotLength > 0) + { + CS_BlobIndex blob; + blob.type = BE(CSSLOT_ENTITLEMENTS); + blob.offset = BE(uSuperBlobHeaderLength + uCodeDirectorySlotLength + uRequirementsSlotLength); + arrBlobIndexes.push_back(blob); + } + if (uDerEntitlementsLength > 0) + { + CS_BlobIndex blob; + blob.type = BE(CSSLOT_DER_ENTITLEMENTS); + blob.offset = BE(uSuperBlobHeaderLength + uCodeDirectorySlotLength + uRequirementsSlotLength + uEntitlementsSlotLength); + arrBlobIndexes.push_back(blob); + } + if (uAltnateCodeDirectorySlotLength > 0) + { + CS_BlobIndex blob; + blob.type = BE(CSSLOT_ALTERNATE_CODEDIRECTORIES); + blob.offset = BE(uSuperBlobHeaderLength + uCodeDirectorySlotLength + uRequirementsSlotLength + uEntitlementsSlotLength + uDerEntitlementsLength); + arrBlobIndexes.push_back(blob); + } + if (uCMSSignatureSlotLength > 0) + { + CS_BlobIndex blob; + blob.type = BE(CSSLOT_SIGNATURESLOT); + blob.offset = BE(uSuperBlobHeaderLength + uCodeDirectorySlotLength + uRequirementsSlotLength + uEntitlementsSlotLength + uDerEntitlementsLength + uAltnateCodeDirectorySlotLength); + arrBlobIndexes.push_back(blob); + } + + CS_SuperBlob superblob; + superblob.magic = BE(CSMAGIC_EMBEDDED_SIGNATURE); + superblob.length = BE(uCodeSignLength); + superblob.count = BE(uCodeSignBlobCount); + + strOutput.clear(); + strOutput.reserve(uCodeSignLength); + strOutput.append((const char *)&superblob, sizeof(superblob)); + for (size_t i = 0; i < arrBlobIndexes.size(); i++) + { + CS_BlobIndex &blob = arrBlobIndexes[i]; + strOutput.append((const char *)&blob, sizeof(blob)); + } + strOutput += strCodeDirectorySlot; + strOutput += strRequirementsSlot; + strOutput += strEntitlementsSlot; + strOutput += strDerEntitlementsSlot; + strOutput += strAltnateCodeDirectorySlot; + strOutput += strCMSSignatureSlot; + + if (ZLog::IsDebug()) + { + WriteFile("./.zsign_debug/Requirements.slot.new", strRequirementsSlot); + WriteFile("./.zsign_debug/Entitlements.slot.new", strEntitlementsSlot); + WriteFile("./.zsign_debug/Entitlements.der.slot.new", strDerEntitlementsSlot); + WriteFile("./.zsign_debug/Entitlements.plist.new", strEntitlementsSlot.data() + 8, strEntitlementsSlot.size() - 8); + WriteFile("./.zsign_debug/CodeDirectory_SHA1.slot.new", strCodeDirectorySlot); + WriteFile("./.zsign_debug/CodeDirectory_SHA256.slot.new", strAltnateCodeDirectorySlot); + WriteFile("./.zsign_debug/CMSSignature.slot.new", strCMSSignatureSlot); + WriteFile("./.zsign_debug/CMSSignature.der.new", strCMSSignatureSlot.data() + 8, strCMSSignatureSlot.size() - 8); + WriteFile("./.zsign_debug/CodeSignature.blob.new", strOutput); + } + + return true; +} + +bool ZArchO::Sign(ZSignAsset *pSignAsset, bool bForce, const string &strBundleId, const string &strInfoPlistSHA1, const string &strInfoPlistSHA256, const string &strCodeResourcesData) +{ + if (NULL == m_pSignBase) + { + m_bEnoughSpace = false; + ZLog::Warn(">>> Can't Find CodeSignature Segment!\n"); + return false; + } + + string strCodeResourcesSHA1; + string strCodeResourcesSHA256; + if (strCodeResourcesData.empty()) + { + strCodeResourcesSHA1.append(20, 0); + strCodeResourcesSHA256.append(32, 0); + } + else + { + SHASum(strCodeResourcesData, strCodeResourcesSHA1, strCodeResourcesSHA256); + } + + string strCodeSignBlob; + BuildCodeSignature(pSignAsset, bForce, strBundleId, strInfoPlistSHA1, strInfoPlistSHA256, strCodeResourcesSHA1, strCodeResourcesSHA256, strCodeSignBlob); + if (strCodeSignBlob.empty()) + { + ZLog::Error(">>> Build CodeSignature Failed!\n"); + return false; + } + + int nSpaceLength = (int)m_uLength - (int)m_uCodeLength - (int)strCodeSignBlob.size(); + if (nSpaceLength < 0) + { + m_bEnoughSpace = false; + ZLog::WarnV(">>> No Enough CodeSignature Space. Length => Now: %d, Need: %d\n", (int)m_uLength - (int)m_uCodeLength, (int)strCodeSignBlob.size()); + return false; + } + + memcpy(m_pBase + m_uCodeLength, strCodeSignBlob.data(), strCodeSignBlob.size()); + //memset(m_pBase + m_uCodeLength + strCodeSignBlob.size(), 0, nSpaceLength); + return true; +} + +uint32_t ZArchO::ReallocCodeSignSpace(const string &strNewFile) +{ + RemoveFile(strNewFile.c_str()); + + uint32_t uNewLength = m_uCodeLength + ByteAlign(((m_uCodeLength / 4096) + 1) * (20 + 32), 4096) + 16384; //16K May Be Enough + if (NULL == m_pLinkEditSegment || uNewLength <= m_uLength) + { + return 0; + } + + load_command *pseglc = (load_command *)m_pLinkEditSegment; + switch (BO(pseglc->cmd)) + { + case LC_SEGMENT: + { + segment_command *seglc = (segment_command *)m_pLinkEditSegment; + seglc->vmsize = ByteAlign(BO(seglc->vmsize) + (uNewLength - m_uLength), 4096); + seglc->vmsize = BO(seglc->vmsize); + seglc->filesize = uNewLength - BO(seglc->fileoff); + seglc->filesize = BO(seglc->filesize); + } + break; + case LC_SEGMENT_64: + { + segment_command_64 *seglc = (segment_command_64 *)m_pLinkEditSegment; + seglc->vmsize = ByteAlign(BO((uint32_t)seglc->vmsize) + (uNewLength - m_uLength), 4096); + seglc->vmsize = BO((uint32_t)seglc->vmsize); + seglc->filesize = uNewLength - BO((uint32_t)seglc->fileoff); + seglc->filesize = BO((uint32_t)seglc->filesize); + } + break; + } + + codesignature_command *pcslc = (codesignature_command *)m_pCodeSignSegment; + if (NULL == pcslc) + { + if (m_uLoadCommandsFreeSpace < 4) + { + ZLog::Error(">>> Can't Find Free Space Of LoadCommands For CodeSignature!\n"); + return 0; + } + + pcslc = (codesignature_command *)(m_pBase + m_uHeaderSize + BO(m_pHeader->sizeofcmds)); + pcslc->cmd = BO(LC_CODE_SIGNATURE); + pcslc->cmdsize = BO((uint32_t)sizeof(codesignature_command)); + pcslc->dataoff = BO(m_uCodeLength); + m_pHeader->ncmds = BO(BO(m_pHeader->ncmds) + 1); + m_pHeader->sizeofcmds = BO(BO(m_pHeader->sizeofcmds) + sizeof(codesignature_command)); + } + pcslc->datasize = BO(uNewLength - m_uCodeLength); + + if (!AppendFile(strNewFile.c_str(), (const char *)m_pBase, m_uLength)) + { + return 0; + } + + string strPadding; + strPadding.append(uNewLength - m_uLength, 0); + if (!AppendFile(strNewFile.c_str(), strPadding)) + { + RemoveFile(strNewFile.c_str()); + return 0; + } + + return uNewLength; +} + +bool ZArchO::InjectDyLib(bool bWeakInject, const char *szDyLibPath, bool &bCreate) +{ + if (NULL == m_pHeader) + { + return false; + } + + uint8_t *pLoadCommand = m_pBase + m_uHeaderSize; + for (uint32_t i = 0; i < BO(m_pHeader->ncmds); i++) + { + load_command *plc = (load_command *)pLoadCommand; + uint32_t uLoadType = BO(plc->cmd); + if (LC_LOAD_DYLIB == uLoadType || LC_LOAD_WEAK_DYLIB == uLoadType) + { + dylib_command *dlc = (dylib_command *)pLoadCommand; + const char *szDyLib = (const char *)(pLoadCommand + BO(dlc->dylib.name.offset)); + if (0 == strcmp(szDyLib, szDyLibPath)) + { + if ((bWeakInject && (LC_LOAD_WEAK_DYLIB != uLoadType)) || (!bWeakInject && (LC_LOAD_DYLIB != uLoadType))) + { + dlc->cmd = BO((uint32_t)(bWeakInject ? LC_LOAD_WEAK_DYLIB : LC_LOAD_DYLIB)); + ZLog::WarnV(">>> DyLib Load Type Changed! %s -> %s\n", (LC_LOAD_DYLIB == uLoadType) ? "LC_LOAD_DYLIB" : "LC_LOAD_WEAK_DYLIB", bWeakInject ? "LC_LOAD_WEAK_DYLIB" : "LC_LOAD_DYLIB"); + } + else + { + ZLog::WarnV(">>> DyLib Is Already Existed! %s\n"); + } + return true; + } + } + pLoadCommand += BO(plc->cmdsize); + } + + uint32_t uDylibPathLength = (uint32_t)strlen(szDyLibPath); + uint32_t uDylibPathPadding = (8 - uDylibPathLength % 8); + uint32_t uDyLibCommandSize = sizeof(dylib_command) + uDylibPathLength + uDylibPathPadding; + if (m_uLoadCommandsFreeSpace > 0 && m_uLoadCommandsFreeSpace < uDyLibCommandSize) // some bin doesn't have '__text' + { + ZLog::Error(">>> Can't Find Free Space Of LoadCommands For LC_LOAD_DYLIB Or LC_LOAD_WEAK_DYLIB!\n"); + return false; + } + + //add + dylib_command *dlc = (dylib_command *)(m_pBase + m_uHeaderSize + BO(m_pHeader->sizeofcmds)); + dlc->cmd = BO((uint32_t)(bWeakInject ? LC_LOAD_WEAK_DYLIB : LC_LOAD_DYLIB)); + dlc->cmdsize = BO(uDyLibCommandSize); + dlc->dylib.name.offset = BO((uint32_t)sizeof(dylib_command)); + dlc->dylib.timestamp = BO((uint32_t)2); + dlc->dylib.current_version = 0; + dlc->dylib.compatibility_version = 0; + + string strDylibPath = szDyLibPath; + strDylibPath.append(uDylibPathPadding, 0); + + uint8_t *pDyLibPath = (uint8_t *)dlc + sizeof(dylib_command); + memcpy(pDyLibPath, strDylibPath.data(), uDylibPathLength + uDylibPathPadding); + + m_pHeader->ncmds = BO(BO(m_pHeader->ncmds) + 1); + m_pHeader->sizeofcmds = BO(BO(m_pHeader->sizeofcmds) + uDyLibCommandSize); + + bCreate = true; + return true; +} + +bool ZArchO::ChangeDylibPath(const char *oldPath, const char *newPath) { + if (NULL == m_pHeader) { + return false; + } + + uint8_t *pLoadCommand = m_pBase + m_uHeaderSize; + bool pathChanged = false; + uint32_t oldPathLength = (uint32_t)strlen(oldPath); + uint32_t newPathLength = (uint32_t)strlen(newPath); + uint32_t oldPathPadding = (8 - oldPathLength % 8) % 8; + uint32_t newPathPadding = (8 - newPathLength % 8) % 8; + uint32_t newLoadCommandSize = 0; + + for (uint32_t i = 0; i < BO(m_pHeader->ncmds); i++) { + load_command *plc = (load_command *)pLoadCommand; + uint32_t uLoadType = BO(plc->cmd); + + if (LC_LOAD_DYLIB == uLoadType || LC_LOAD_WEAK_DYLIB == uLoadType) { + dylib_command *dlc = (dylib_command *)pLoadCommand; + const char *szDyLib = (const char *)(pLoadCommand + BO(dlc->dylib.name.offset)); + + if (strcmp(szDyLib, oldPath) == 0) { + uint32_t dylibPathOffset = sizeof(dylib_command); + uint32_t dylibPathSize = newPathLength + newPathPadding; + if (dylibPathOffset + dylibPathSize > BO(plc->cmdsize)) { + ZLog::Error(">>> Insufficient space to update dylib path!\n"); + return false; + } + + memcpy(pLoadCommand + dylibPathOffset, newPath, newPathLength); + memset(pLoadCommand + dylibPathOffset + newPathLength, 0, newPathPadding); + + ZLog::PrintV(">>> Dylib Path Changed: %s -> %s\n", oldPath, newPath); + + pathChanged = true; + } + } + + pLoadCommand += BO(plc->cmdsize); + } + + if (!pathChanged) { + ZLog::PrintV(">>> Old Dylib Path Not Found: %s\n", oldPath); + } + + return pathChanged; +} + + + + + + +void ZArchO::uninstallDylibs(set dylibNames) +{ + uint8_t *pLoadCommand = m_pBase + m_uHeaderSize; + uint32_t old_load_command_size = m_pHeader->sizeofcmds; + uint8_t *new_load_command_data = (uint8_t*)malloc(old_load_command_size); + memset(new_load_command_data,0,old_load_command_size); + uint32_t new_load_command_size = 0; + uint32_t clear_num = 0; + uint32_t clear_data_size = 0; + for (uint32_t i = 0; i < BO(m_pHeader->ncmds); i++) + { + load_command *plc = (load_command *)pLoadCommand; + uint32_t load_command_size = BO(plc->cmdsize); + if (LC_LOAD_DYLIB == BO(plc->cmd) || LC_LOAD_WEAK_DYLIB == BO(plc->cmd)) + { + dylib_command *dlc = (dylib_command *)pLoadCommand; + const char *szDyLib = (const char *)(pLoadCommand + BO(dlc->dylib.name.offset)); + string dylibName = szDyLib; + if(dylibNames.count(dylibName)>0){ + ZLog::PrintV("\t\t\t%s\tclear\n", szDyLib); + clear_num++; + clear_data_size+=load_command_size; + pLoadCommand += BO(plc->cmdsize); + continue; + } + ZLog::PrintV("\t\t\t%s\n", szDyLib); + } + new_load_command_size+=load_command_size; + memcpy(new_load_command_data,pLoadCommand,load_command_size); + new_load_command_data += load_command_size; + pLoadCommand += BO(plc->cmdsize); + } + pLoadCommand -= m_pHeader->sizeofcmds; + + m_pHeader->ncmds -= clear_num; + m_pHeader->sizeofcmds -= clear_data_size; + new_load_command_data -=new_load_command_size; + memset(pLoadCommand,0,old_load_command_size); + memcpy(pLoadCommand,new_load_command_data,new_load_command_size); + free(new_load_command_data); +} + +std::vector ZArchO::ListDylibs() { + std::vector dylibList; + uint8_t *pLoadCommand = m_pBase + m_uHeaderSize; + + for (uint32_t i = 0; i < BO(m_pHeader->ncmds); i++) { + load_command *plc = (load_command *)pLoadCommand; + if (LC_LOAD_DYLIB == BO(plc->cmd) || LC_LOAD_WEAK_DYLIB == BO(plc->cmd)) { + dylib_command *dlc = (dylib_command *)pLoadCommand; + const char *szDyLib = (const char *)(pLoadCommand + BO(dlc->dylib.name.offset)); + dylibList.push_back(std::string(szDyLib)); + } + pLoadCommand += BO(plc->cmdsize); + } + + return dylibList; +} + diff --git a/ZSign/archo.h b/ZSign/archo.h new file mode 100644 index 0000000..b9dc27d --- /dev/null +++ b/ZSign/archo.h @@ -0,0 +1,42 @@ +#pragma once +#include "common/mach-o.h" +#include "openssl.h" +#include +class ZArchO +{ +public: + ZArchO(); + bool Init(uint8_t *pBase, uint32_t uLength); + +public: + bool Sign(ZSignAsset *pSignAsset, bool bForce, const string &strBundleId, const string &strInfoPlistSHA1, const string &strInfoPlistSHA256, const string &strCodeResourcesData); + void PrintInfo(); + bool IsExecute(); + bool InjectDyLib(bool bWeakInject, const char *szDyLibPath, bool &bCreate); + uint32_t ReallocCodeSignSpace(const string &strNewFile); + void uninstallDylibs(set dylibNames); + bool ChangeDylibPath(const char *oldPath, const char *newPath); + std::vector ListDylibs(); +private: + uint32_t BO(uint32_t uVal); + const char *GetFileType(uint32_t uFileType); + const char *GetArch(int cpuType, int cpuSubType); + bool BuildCodeSignature(ZSignAsset *pSignAsset, bool bForce, const string &strBundleId, const string &strInfoPlistSHA1, const string &strInfoPlistSHA256, const string &strCodeResourcesSHA1, const string &strCodeResourcesSHA256, string &strOutput); + +public: + uint8_t *m_pBase; + uint32_t m_uLength; + uint32_t m_uCodeLength; + uint8_t *m_pSignBase; + uint32_t m_uSignLength; + string m_strInfoPlist; + bool m_bEncrypted; + bool m_b64; + bool m_bBigEndian; + bool m_bEnoughSpace; + uint8_t *m_pCodeSignSegment; + uint8_t *m_pLinkEditSegment; + uint32_t m_uLoadCommandsFreeSpace; + mach_header *m_pHeader; + uint32_t m_uHeaderSize; +}; diff --git a/ZSign/bundle.cpp b/ZSign/bundle.cpp new file mode 100644 index 0000000..cc2a45f --- /dev/null +++ b/ZSign/bundle.cpp @@ -0,0 +1,710 @@ +#include "bundle.h" +#include "macho.h" +#include "sys/stat.h" +#include "sys/types.h" +#include "common/base64.h" +#include "common/common.h" + +ZAppBundle::ZAppBundle() +{ + m_pSignAsset = NULL; + m_bForceSign = false; + m_bWeakInject = false; +} + +bool ZAppBundle::FindAppFolder(const string &strFolder, string &strAppFolder) +{ + if (IsPathSuffix(strFolder, ".app") || IsPathSuffix(strFolder, ".appex")) + { + strAppFolder = strFolder; + return true; + } + + DIR *dir = opendir(strFolder.c_str()); + if (NULL != dir) + { + dirent *ptr = readdir(dir); + while (NULL != ptr) + { + if (0 != strcmp(ptr->d_name, ".") && 0 != strcmp(ptr->d_name, "..") && 0 != strcmp(ptr->d_name, "__MACOSX")) + { + bool isdir = false; + if (DT_DIR == ptr->d_type) + { + isdir = true; + } + else if (DT_UNKNOWN == ptr->d_type) + { + // Entry type can be unknown depending on the underlying file system + ZLog::DebugV(">>> Unknown directory entry type for %s, falling back to POSIX-compatible check\n", strFolder.c_str()); + struct stat statbuf; + stat(strFolder.c_str(), &statbuf); + if (S_ISDIR(statbuf.st_mode)) + { + isdir = true; + } + } + if (isdir) + { + string strSubFolder = strFolder; + strSubFolder += "/"; + strSubFolder += ptr->d_name; + if (FindAppFolder(strSubFolder, strAppFolder)) + { + return true; + } + } + } + ptr = readdir(dir); + } + closedir(dir); + } + return false; +} + +bool ZAppBundle::GetSignFolderInfo(const string &strFolder, JValue &jvNode, bool bGetName) +{ + JValue jvInfo; + string strInfoPlistData; + string strInfoPlistPath = strFolder + "/Info.plist"; + ReadFile(strInfoPlistPath.c_str(), strInfoPlistData); + jvInfo.readPList(strInfoPlistData); + string strBundleId = jvInfo["CFBundleIdentifier"]; + string strBundleExe = jvInfo["CFBundleExecutable"]; + string strBundleVersion = jvInfo["CFBundleVersion"]; + if (strBundleId.empty() || strBundleExe.empty()) + { + return false; + } + + string strInfoPlistSHA1Base64; + string strInfoPlistSHA256Base64; + SHASumBase64(strInfoPlistData, strInfoPlistSHA1Base64, strInfoPlistSHA256Base64); + + jvNode["bid"] = strBundleId; + jvNode["bver"] = strBundleVersion; + jvNode["exec"] = strBundleExe; + jvNode["sha1"] = strInfoPlistSHA1Base64; + jvNode["sha2"] = strInfoPlistSHA256Base64; + + if (bGetName) + { + string strBundleName = jvInfo["CFBundleDisplayName"]; + if (strBundleName.empty()) + { + strBundleName = jvInfo["CFBundleName"].asCString(); + } + jvNode["name"] = strBundleName; + } + + return true; +} + +bool ZAppBundle::GetObjectsToSign(const string &strFolder, JValue &jvInfo) +{ + DIR *dir = opendir(strFolder.c_str()); + if (NULL != dir) + { + dirent *ptr = readdir(dir); + while (NULL != ptr) + { + if (0 != strcmp(ptr->d_name, ".") && 0 != strcmp(ptr->d_name, "..")) + { + string strNode = strFolder + "/" + ptr->d_name; + if (DT_DIR == ptr->d_type) + { + if (IsPathSuffix(strNode, ".app") || IsPathSuffix(strNode, ".appex") || IsPathSuffix(strNode, ".framework") || IsPathSuffix(strNode, ".xctest")) + { + JValue jvNode; + jvNode["path"] = strNode.substr(m_strAppFolder.size() + 1); + if (GetSignFolderInfo(strNode, jvNode)) + { + if (GetObjectsToSign(strNode, jvNode)) + { + jvInfo["folders"].push_back(jvNode); + } + } + } + else + { + GetObjectsToSign(strNode, jvInfo); + } + } + else if (DT_REG == ptr->d_type) + { + if (IsPathSuffix(strNode, ".dylib")) + { + jvInfo["files"].push_back(strNode.substr(m_strAppFolder.size() + 1)); + } + } + } + ptr = readdir(dir); + } + closedir(dir); + } + return true; +} + +void ZAppBundle::GetFolderFiles(const string &strFolder, const string &strBaseFolder, set &setFiles) +{ + DIR *dir = opendir(strFolder.c_str()); + if (NULL != dir) + { + dirent *ptr = readdir(dir); + while (NULL != ptr) + { + if (0 != strcmp(ptr->d_name, ".") && 0 != strcmp(ptr->d_name, "..")) + { + string strNode = strFolder; + strNode += "/"; + strNode += ptr->d_name; + if (DT_DIR == ptr->d_type) + { + GetFolderFiles(strNode, strBaseFolder, setFiles); + } + else if (DT_REG == ptr->d_type) + { + setFiles.insert(strNode.substr(strBaseFolder.size() + 1)); + } + } + ptr = readdir(dir); + } + closedir(dir); + } +} + +bool ZAppBundle::GenerateCodeResources(const string &strFolder, JValue &jvCodeRes) +{ + jvCodeRes.clear(); + + set setFiles; + GetFolderFiles(strFolder, strFolder, setFiles); + + JValue jvInfo; + string strInfoPlistPath = strFolder + "/Info.plist"; + jvInfo.readPListFile(strInfoPlistPath.c_str()); + string strBundleExe = jvInfo["CFBundleExecutable"]; + setFiles.erase(strBundleExe); + setFiles.erase("_CodeSignature/CodeResources"); + + jvCodeRes["files"] = JValue(JValue::E_OBJECT); + jvCodeRes["files2"] = JValue(JValue::E_OBJECT); + + for (set::iterator it = setFiles.begin(); it != setFiles.end(); it++) + { + string strKey = *it; + string strFile = strFolder + "/" + strKey; + string strFileSHA1Base64; + string strFileSHA256Base64; + SHASumBase64File(strFile.c_str(), strFileSHA1Base64, strFileSHA256Base64); + + bool bomit1 = false; + bool bomit2 = false; + + if ("Info.plist" == strKey || "PkgInfo" == strKey) + { + bomit2 = true; + } + + if (IsPathSuffix(strKey, ".DS_Store")) + { + bomit2 = true; + } + + if (IsPathSuffix(strKey, ".lproj/locversion.plist")) + { + bomit1 = true; + bomit2 = true; + } + + if (!bomit1) + { + if (string::npos != strKey.rfind(".lproj/")) + { + jvCodeRes["files"][strKey]["hash"] = "data:" + strFileSHA1Base64; + jvCodeRes["files"][strKey]["optional"] = true; + } + else + { + jvCodeRes["files"][strKey] = "data:" + strFileSHA1Base64; + } + } + + if (!bomit2) + { + jvCodeRes["files2"][strKey]["hash"] = "data:" + strFileSHA1Base64; + jvCodeRes["files2"][strKey]["hash2"] = "data:" + strFileSHA256Base64; + if (string::npos != strKey.rfind(".lproj/")) + { + jvCodeRes["files2"][strKey]["optional"] = true; + } + } + } + + jvCodeRes["rules"]["^.*"] = true; + jvCodeRes["rules"]["^.*\\.lproj/"]["optional"] = true; + jvCodeRes["rules"]["^.*\\.lproj/"]["weight"] = 1000.0; + jvCodeRes["rules"]["^.*\\.lproj/locversion.plist$"]["omit"] = true; + jvCodeRes["rules"]["^.*\\.lproj/locversion.plist$"]["weight"] = 1100.0; + jvCodeRes["rules"]["^Base\\.lproj/"]["weight"] = 1010.0; + jvCodeRes["rules"]["^version.plist$"] = true; + + jvCodeRes["rules2"]["^.*"] = true; + jvCodeRes["rules2"][".*\\.dSYM($|/)"]["weight"] = 11.0; + jvCodeRes["rules2"]["^(.*/)?\\.DS_Store$"]["omit"] = true; + jvCodeRes["rules2"]["^(.*/)?\\.DS_Store$"]["weight"] = 2000.0; + jvCodeRes["rules2"]["^.*\\.lproj/"]["optional"] = true; + jvCodeRes["rules2"]["^.*\\.lproj/"]["weight"] = 1000.0; + jvCodeRes["rules2"]["^.*\\.lproj/locversion.plist$"]["omit"] = true; + jvCodeRes["rules2"]["^.*\\.lproj/locversion.plist$"]["weight"] = 1100.0; + jvCodeRes["rules2"]["^Base\\.lproj/"]["weight"] = 1010.0; + jvCodeRes["rules2"]["^Info\\.plist$"]["omit"] = true; + jvCodeRes["rules2"]["^Info\\.plist$"]["weight"] = 20.0; + jvCodeRes["rules2"]["^PkgInfo$"]["omit"] = true; + jvCodeRes["rules2"]["^PkgInfo$"]["weight"] = 20.0; + jvCodeRes["rules2"]["^embedded\\.provisionprofile$"]["weight"] = 20.0; + jvCodeRes["rules2"]["^version\\.plist$"]["weight"] = 20.0; + + return true; +} + +void ZAppBundle::GetChangedFiles(JValue &jvNode, vector &arrChangedFiles) +{ + if (jvNode.has("files")) + { + for (size_t i = 0; i < jvNode["files"].size(); i++) + { + arrChangedFiles.push_back(jvNode["files"][i]); + } + } + + if (jvNode.has("folders")) + { + for (size_t i = 0; i < jvNode["folders"].size(); i++) + { + JValue &jvSubNode = jvNode["folders"][i]; + GetChangedFiles(jvSubNode, arrChangedFiles); + string strPath = jvSubNode["path"]; + arrChangedFiles.push_back(strPath + "/_CodeSignature/CodeResources"); + arrChangedFiles.push_back(strPath + "/" + jvSubNode["exec"].asString()); + } + } +} + +void ZAppBundle::GetNodeChangedFiles(JValue &jvNode, bool dontGenerateEmbeddedMobileProvision) +{ + if (jvNode.has("folders")) + { + for (size_t i = 0; i < jvNode["folders"].size(); i++) + { + GetNodeChangedFiles(jvNode["folders"][i], dontGenerateEmbeddedMobileProvision); + } + } + + vector arrChangedFiles; + GetChangedFiles(jvNode, arrChangedFiles); + for (size_t i = 0; i < arrChangedFiles.size(); i++) + { + jvNode["changed"].push_back(arrChangedFiles[i]); + } +// TODO: try + if (dontGenerateEmbeddedMobileProvision) { + if ("/" == jvNode["path"]) + { //root + jvNode["changed"].push_back("embedded.mobileprovision"); + } + } +} + +int ZAppBundle::GetSignCount(JValue &jvNode) { + int ans = 1; + if (jvNode.has("files")) + { + ans += jvNode["files"].size(); + } + + if (jvNode.has("folders")) + { + for (size_t i = 0; i < jvNode["folders"].size(); i++) + { + ans += GetSignCount(jvNode["folders"][i]); + } + } + return ans; +} + +bool ZAppBundle::SignNode(JValue &jvNode) +{ + if (jvNode.has("folders")) + { + for (size_t i = 0; i < jvNode["folders"].size(); i++) + { + if (!SignNode(jvNode["folders"][i])) + { + return false; + } + } + } + + if (jvNode.has("files")) + { + for (size_t i = 0; i < jvNode["files"].size(); i++) + { + const char *szFile = jvNode["files"][i].asCString(); + ZLog::PrintV(">>> SignFile: \t%s\n", szFile); + ZMachO macho; + if (!macho.InitV("%s/%s", m_strAppFolder.c_str(), szFile)) + { + return false; + } + + if (!macho.Sign(m_pSignAsset, m_bForceSign, mainBundleIdentifier, "", "", "")) + { + return false; + } + if(progressHandler) { + progressHandler(); + } + } + } + + ZBase64 b64; + string strInfoPlistSHA1; + string strInfoPlistSHA256; + string strFolder = jvNode["path"]; + string strBundleId = jvNode["bid"]; + string strBundleExe = jvNode["exec"]; + b64.Decode(jvNode["sha1"].asCString(), strInfoPlistSHA1); + b64.Decode(jvNode["sha2"].asCString(), strInfoPlistSHA256); + if (strBundleId.empty() || strBundleExe.empty() || strInfoPlistSHA1.empty() || strInfoPlistSHA256.empty()) + { + ZLog::ErrorV(">>> Can't Get BundleID or BundleExecute or Info.plist SHASum in Info.plist! %s\n", strFolder.c_str()); + return false; + } + + string strBaseFolder = m_strAppFolder; + if ("/" != strFolder) + { + strBaseFolder += "/"; + strBaseFolder += strFolder; + } + + string strExePath = strBaseFolder + "/" + strBundleExe; + ZLog::PrintV(">>> SignFolder: %s, (%s)\n", ("/" == strFolder) ? basename((char *)m_strAppFolder.c_str()) : strFolder.c_str(), strBundleExe.c_str()); + + ZMachO macho; + if (!macho.Init(strExePath.c_str())) + { + ZLog::ErrorV(">>> Can't Parse BundleExecute File! %s\n", strExePath.c_str()); + return false; + } + + RemoveFolderV("%s/_CodeSignature", strBaseFolder.c_str()); + CreateFolderV("%s/_CodeSignature", strBaseFolder.c_str()); + string strCodeResFile = strBaseFolder + "/_CodeSignature/CodeResources"; + + JValue jvCodeRes; + if (!m_bForceSign) + { + jvCodeRes.readPListFile(strCodeResFile.c_str()); + } + + if (m_bForceSign || jvCodeRes.isNull()) + { //create + if (!GenerateCodeResources(strBaseFolder, jvCodeRes)) + { + ZLog::ErrorV(">>> Create CodeResources Failed! %s\n", strBaseFolder.c_str()); + return false; + } + } + else if (jvNode.has("changed")) + { //use existsed + for (size_t i = 0; i < jvNode["changed"].size(); i++) + { + string strFile = jvNode["changed"][i].asCString(); + string strRealFile = m_strAppFolder + "/" + strFile; + + string strFileSHA1Base64; + string strFileSHA256Base64; + if (!SHASumBase64File(strRealFile.c_str(), strFileSHA1Base64, strFileSHA256Base64)) + { + ZLog::ErrorV(">>> Can't Get Changed File SHASumBase64! %s", strFile.c_str()); + return false; + } + + string strKey = strFile; + if ("/" != strFolder) + { + strKey = strFile.substr(strFolder.size() + 1); + } + jvCodeRes["files"][strKey] = "data:" + strFileSHA1Base64; + jvCodeRes["files2"][strKey]["hash"] = "data:" + strFileSHA1Base64; + jvCodeRes["files2"][strKey]["hash2"] = "data:" + strFileSHA256Base64; + + ZLog::DebugV("\t\tChanged File: %s, %s\n", strFileSHA1Base64.c_str(), strKey.c_str()); + } + } + + string strCodeResData; + jvCodeRes.writePList(strCodeResData); + if (!WriteFile(strCodeResFile.c_str(), strCodeResData)) + { + ZLog::ErrorV("\tWriting CodeResources Failed! %s\n", strCodeResFile.c_str()); + return false; + } + + bool bForceSign = m_bForceSign; + if ("/" == strFolder && !m_strDyLibPath.empty()) + { //inject dylib + macho.InjectDyLib(m_bWeakInject, m_strDyLibPath.c_str(), bForceSign); + } + + if (!macho.Sign(m_pSignAsset, bForceSign, strBundleId, strInfoPlistSHA1, strInfoPlistSHA256, strCodeResData)) + { + return false; + } + + if(progressHandler) { + progressHandler(); + } + return true; +} + +void ZAppBundle::GetPlugIns(const string &strFolder, vector &arrPlugIns) +{ + DIR *dir = opendir(strFolder.c_str()); + if (NULL != dir) + { + dirent *ptr = readdir(dir); + while (NULL != ptr) + { + if (0 != strcmp(ptr->d_name, ".") && 0 != strcmp(ptr->d_name, "..")) + { + if (DT_DIR == ptr->d_type) + { + string strSubFolder = strFolder; + strSubFolder += "/"; + strSubFolder += ptr->d_name; + if (IsPathSuffix(strSubFolder, ".app") || IsPathSuffix(strSubFolder, ".appex")) + { + arrPlugIns.push_back(strSubFolder); + } + GetPlugIns(strSubFolder, arrPlugIns); + } + } + ptr = readdir(dir); + } + closedir(dir); + } +} + +bool ZAppBundle::ConfigureFolderSign(ZSignAsset *pSignAsset, + const string &strFolder, + const string &execName, + const string &strBundleID, + const string &strBundleVersion, + const string &strDisplayName, + const string &strDyLibFile, + bool bForce, + bool bWeakInject, + bool bEnableCache, + bool dontGenerateEmbeddedMobileProvision + ) +{ + m_bForceSign = bForce; + m_pSignAsset = pSignAsset; + m_bWeakInject = bWeakInject; + if (NULL == m_pSignAsset) + { + return false; + } + + if (!FindAppFolder(strFolder, m_strAppFolder)) + { + ZLog::ErrorV(">>> Can't Find App Folder! %s\n", strFolder.c_str()); + return false; + } + + JValue jvInfoPlist; + bool jvInfoReadSuccess = jvInfoPlist.readPListPath("%s/Info.plist", m_strAppFolder.c_str()); + if (!strBundleID.empty() || !strDisplayName.empty() || !strBundleVersion.empty()) + { //modify bundle id + + if (jvInfoReadSuccess) + { + m_bForceSign = true; + if (!strBundleID.empty()) + { + string strOldBundleID = jvInfoPlist["CFBundleIdentifier"]; + jvInfoPlist["CFBundleIdentifier"] = strBundleID; + ZLog::PrintV(">>> BundleId: \t%s -> %s\n", strOldBundleID.c_str(), strBundleID.c_str()); + + //modify plugins bundle id + vector arrPlugIns; + GetPlugIns(m_strAppFolder, arrPlugIns); + for (size_t i = 0; i < arrPlugIns.size(); i++) + { + string &strPlugin = arrPlugIns[i]; + JValue jvPlugInInfoPlist; + if (jvPlugInInfoPlist.readPListPath("%s/Info.plist", strPlugin.c_str())) + { + string strOldPlugInBundleID = jvPlugInInfoPlist["CFBundleIdentifier"]; + string strNewPlugInBundleID = strOldPlugInBundleID; + StringReplace(strNewPlugInBundleID, strOldBundleID, strBundleID); + jvPlugInInfoPlist["CFBundleIdentifier"] = strNewPlugInBundleID; + ZLog::PrintV(">>> BundleId: \t%s -> %s, PlugIn\n", strOldPlugInBundleID.c_str(), strNewPlugInBundleID.c_str()); + + if (jvPlugInInfoPlist.has("WKCompanionAppBundleIdentifier")) + { + string strOldWKCBundleID = jvPlugInInfoPlist["WKCompanionAppBundleIdentifier"]; + string strNewWKCBundleID = strOldWKCBundleID; + StringReplace(strNewWKCBundleID, strOldBundleID, strBundleID); + jvPlugInInfoPlist["WKCompanionAppBundleIdentifier"] = strNewWKCBundleID; + ZLog::PrintV(">>> BundleId: \t%s -> %s, PlugIn-WKCompanionAppBundleIdentifier\n", strOldWKCBundleID.c_str(), strNewWKCBundleID.c_str()); + } + + if (jvPlugInInfoPlist.has("NSExtension")) + { + if (jvPlugInInfoPlist["NSExtension"].has("NSExtensionAttributes")) + { + if (jvPlugInInfoPlist["NSExtension"]["NSExtensionAttributes"].has("WKAppBundleIdentifier")) + { + string strOldWKBundleID = jvPlugInInfoPlist["NSExtension"]["NSExtensionAttributes"]["WKAppBundleIdentifier"]; + string strNewWKBundleID = strOldWKBundleID; + StringReplace(strNewWKBundleID, strOldBundleID, strBundleID); + jvPlugInInfoPlist["NSExtension"]["NSExtensionAttributes"]["WKAppBundleIdentifier"] = strNewWKBundleID; + ZLog::PrintV(">>> BundleId: \t%s -> %s, NSExtension-NSExtensionAttributes-WKAppBundleIdentifier\n", strOldWKBundleID.c_str(), strNewWKBundleID.c_str()); + } + } + } + + jvPlugInInfoPlist.writePListPath("%s/Info.plist", strPlugin.c_str()); + } + } + } + + if (!strDisplayName.empty()) + { + string strOldDisplayName = jvInfoPlist["CFBundleDisplayName"]; + jvInfoPlist["CFBundleName"] = strDisplayName; + jvInfoPlist["CFBundleDisplayName"] = strDisplayName; + ZLog::PrintV(">>> BundleName: %s -> %s\n", strOldDisplayName.c_str(), strDisplayName.c_str()); + } + + if (!strBundleVersion.empty()) + { + string strOldBundleVersion = jvInfoPlist["CFBundleVersion"]; + jvInfoPlist["CFBundleVersion"] = strBundleVersion; + jvInfoPlist["CFBundleShortVersionString"] = strBundleVersion; + ZLog::PrintV(">>> BundleVersion: %s -> %s\n", strOldBundleVersion.c_str(), strBundleVersion.c_str()); + } + + jvInfoPlist.writePListPath("%s/Info.plist", m_strAppFolder.c_str()); + } + else + { + ZLog::ErrorV(">>> Can't Find App's Info.plist! %s\n", strFolder.c_str()); + return false; + } + } + + if (!strDisplayName.empty()) + { + m_bForceSign = true; + JValue jvInfoPlistStrings; + if (jvInfoPlistStrings.readPListPath("%s/zh_CN.lproj/InfoPlist.strings", m_strAppFolder.c_str())) + { + jvInfoPlistStrings["CFBundleName"] = strDisplayName; + jvInfoPlistStrings["CFBundleDisplayName"] = strDisplayName; + jvInfoPlistStrings.writePListPath("%s/zh_CN.lproj/InfoPlist.strings", m_strAppFolder.c_str()); + } + jvInfoPlistStrings.clear(); + if (jvInfoPlistStrings.readPListPath("%s/zh-Hans.lproj/InfoPlist.strings", m_strAppFolder.c_str())) + { + jvInfoPlistStrings["CFBundleName"] = strDisplayName; + jvInfoPlistStrings["CFBundleDisplayName"] = strDisplayName; + jvInfoPlistStrings.writePListPath("%s/zh-Hans.lproj/InfoPlist.strings", m_strAppFolder.c_str()); + } + } + if (dontGenerateEmbeddedMobileProvision) { + if (!WriteFile(pSignAsset->m_strProvisionData, "%s/embedded.mobileprovision", m_strAppFolder.c_str())) + { //embedded.mobileprovision + ZLog::ErrorV(">>> Can't Write embedded.mobileprovision!\n"); + return false; + } + } + + if (!strDyLibFile.empty()) + { //inject dylib + string strDyLibData; + ReadFile(strDyLibFile.c_str(), strDyLibData); + if (!strDyLibData.empty()) + { + string strFileName = basename((char *)strDyLibFile.c_str()); + if (WriteFile(strDyLibData, "%s/%s", m_strAppFolder.c_str(), strFileName.c_str())) + { + StringFormat(m_strDyLibPath, "@executable_path/%s", strFileName.c_str()); + } + } + } + + string strCacheName; + SHA1Text(m_strAppFolder, strCacheName); + if (!IsFileExistsV("%s/zsign_cache.json", m_strAppFolder.c_str())) + { + m_bForceSign = true; + } + mainBundleIdentifier = string(jvInfoPlist["CFBundleIdentifier"]); + ZLog::PrintV("mainBundleIdentifier = %s", mainBundleIdentifier.c_str()); + + JValue jvRoot; + if (m_bForceSign) + { + jvRoot["path"] = "/"; + jvRoot["root"] = m_strAppFolder; + if (!GetSignFolderInfo(m_strAppFolder, jvRoot, true)) + { + ZLog::ErrorV(">>> Can't Get BundleID, BundleVersion, or BundleExecute in Info.plist! %s\n", m_strAppFolder.c_str()); + return false; + } + if (!GetObjectsToSign(m_strAppFolder, jvRoot)) + { + return false; + } + jvRoot["files"].push_back(execName); + GetNodeChangedFiles(jvRoot, dontGenerateEmbeddedMobileProvision); + } + else + { + jvRoot.readPath("%s/zsign_cache.json", m_strAppFolder.c_str()); + } + + ZLog::PrintV(">>> Signing: \t%s ...\n", m_strAppFolder.c_str()); + ZLog::PrintV(">>> AppName: \t%s\n", jvRoot["name"].asCString()); + ZLog::PrintV(">>> BundleId: \t%s\n", jvRoot["bid"].asCString()); + ZLog::PrintV(">>> BundleVer: \t%s\n", jvRoot["bver"].asCString()); + ZLog::PrintV(">>> TeamId: \t%s\n", m_pSignAsset->m_strTeamId.c_str()); + ZLog::PrintV(">>> SubjectCN: \t%s\n", m_pSignAsset->m_strSubjectCN.c_str()); + ZLog::PrintV(">>> ReadCache: \t%s\n", m_bForceSign ? "NO" : "YES"); + ZLog::PrintV(">>> Exclude MobileProvision: \t%s\n", dontGenerateEmbeddedMobileProvision ? "NO" : "YES"); + + config = jvRoot; + + return true; +} + +int ZAppBundle::GetSignCount() { + return GetSignCount(config); +} + +bool ZAppBundle::StartSign(bool enableCache) { + if (SignNode(config)) + { + if (enableCache) + { + config.styleWritePath("%s/zsign_cache.json", m_strAppFolder.c_str()); + } + return true; + } + return false; +} diff --git a/ZSign/bundle.h b/ZSign/bundle.h new file mode 100644 index 0000000..83d21db --- /dev/null +++ b/ZSign/bundle.h @@ -0,0 +1,42 @@ +#pragma once +#include "common/common.h" +#include "common/json.h" +#include "openssl.h" + +class ZAppBundle +{ +public: + ZAppBundle(); + +public: + bool ConfigureFolderSign(ZSignAsset *pSignAsset, const string &strFolder, const string &execName, const string &strBundleID, const string &strBundleVersion, const string &strDisplayName, const string &strDyLibFile, bool bForce, bool bWeakInject, bool bEnableCache, bool dontGenerateEmbeddedMobileProvision); + bool StartSign(bool enableCache); + int GetSignCount(); +private: + bool SignNode(JValue &jvNode); + void GetNodeChangedFiles(JValue &jvNode, bool dontGenerateEmbeddedMobileProvision); + void GetChangedFiles(JValue &jvNode, vector &arrChangedFiles); + void GetPlugIns(const string &strFolder, vector &arrPlugIns); + int GetSignCount(JValue &jvNode); + +private: + bool FindAppFolder(const string &strFolder, string &strAppFolder); + bool GetObjectsToSign(const string &strFolder, JValue &jvInfo); + bool GetSignFolderInfo(const string &strFolder, JValue &jvNode, bool bGetName = false); + +private: + bool GenerateCodeResources(const string &strFolder, JValue &jvCodeRes); + void GetFolderFiles(const string &strFolder, const string &strBaseFolder, set &setFiles); + +private: + bool m_bForceSign; + bool m_bWeakInject; + string m_strDyLibPath; + ZSignAsset *m_pSignAsset; + string mainBundleIdentifier; + JValue config; + +public: + string m_strAppFolder; + std::function progressHandler; +}; diff --git a/ZSign/common/base64.cpp b/ZSign/common/base64.cpp new file mode 100644 index 0000000..bde88ef --- /dev/null +++ b/ZSign/common/base64.cpp @@ -0,0 +1,203 @@ +#include "base64.h" +#include + +#define B0(a) (a & 0xFF) +#define B1(a) (a >> 8 & 0xFF) +#define B2(a) (a >> 16 & 0xFF) +#define B3(a) (a >> 24 & 0xFF) + +ZBase64::ZBase64(void) +{ +} + +ZBase64::~ZBase64(void) +{ + if (!m_arrEnc.empty()) + { + for (size_t i = 0; i < m_arrEnc.size(); i++) + { + delete[] m_arrEnc[i]; + } + m_arrEnc.clear(); + } + + if (!m_arrDec.empty()) + { + for (size_t i = 0; i < m_arrDec.size(); i++) + { + delete[] m_arrDec[i]; + } + m_arrDec.clear(); + } +} + +char ZBase64::GetB64char(int nIndex) +{ + static const char szTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + if (nIndex >= 0 && nIndex < 64) + { + return szTable[nIndex]; + } + return '='; +} + +int ZBase64::GetB64Index(char ch) +{ + int index = -1; + if (ch >= 'A' && ch <= 'Z') + { + index = ch - 'A'; + } + else if (ch >= 'a' && ch <= 'z') + { + index = ch - 'a' + 26; + } + else if (ch >= '0' && ch <= '9') + { + index = ch - '0' + 52; + } + else if (ch == '+') + { + index = 62; + } + else if (ch == '/') + { + index = 63; + } + return index; +} + +const char *ZBase64::Encode(const char *szSrc, int nSrcLen) +{ + if (0 == nSrcLen) + { + nSrcLen = (int)strlen(szSrc); + } + + if (nSrcLen <= 0) + { + return ""; + } + + char *szEnc = new char[nSrcLen * 3 + 128]; + m_arrEnc.push_back(szEnc); + + int i = 0; + int len = 0; + unsigned char *psrc = (unsigned char *)szSrc; + char *p64 = szEnc; + for (i = 0; i < nSrcLen - 3; i += 3) + { + unsigned long ulTmp = *(unsigned long *)psrc; + int b0 = GetB64char((B0(ulTmp) >> 2) & 0x3F); + int b1 = GetB64char((B0(ulTmp) << 6 >> 2 | B1(ulTmp) >> 4) & 0x3F); + int b2 = GetB64char((B1(ulTmp) << 4 >> 2 | B2(ulTmp) >> 6) & 0x3F); + int b3 = GetB64char((B2(ulTmp) << 2 >> 2) & 0x3F); + *((unsigned long *)p64) = b0 | b1 << 8 | b2 << 16 | b3 << 24; + len += 4; + p64 += 4; + psrc += 3; + } + + if (i < nSrcLen) + { + int rest = nSrcLen - i; + unsigned long ulTmp = 0; + for (int j = 0; j < rest; ++j) + { + *(((unsigned char *)&ulTmp) + j) = *psrc++; + } + p64[0] = GetB64char((B0(ulTmp) >> 2) & 0x3F); + p64[1] = GetB64char((B0(ulTmp) << 6 >> 2 | B1(ulTmp) >> 4) & 0x3F); + p64[2] = rest > 1 ? GetB64char((B1(ulTmp) << 4 >> 2 | B2(ulTmp) >> 6) & 0x3F) : '='; + p64[3] = rest > 2 ? GetB64char((B2(ulTmp) << 2 >> 2) & 0x3F) : '='; + p64 += 4; + len += 4; + } + *p64 = '\0'; + return szEnc; +} + +const char *ZBase64::Encode(const string &strInput) +{ + return Encode(strInput.data(), strInput.size()); +} + +const char *ZBase64::Decode(const char *szSrc, int nSrcLen, int *pDecLen) +{ + if (0 == nSrcLen) + { + nSrcLen = (int)strlen(szSrc); + } + + if (nSrcLen <= 0) + { + return ""; + } + + char *szDec = new char[nSrcLen]; + m_arrDec.push_back(szDec); + + int i = 0; + int len = 0; + unsigned char *psrc = (unsigned char *)szSrc; + char *pbuf = szDec; + for (i = 0; i < nSrcLen - 4; i += 4) + { + unsigned long ulTmp = *(unsigned long *)psrc; + + int b0 = (GetB64Index((char)B0(ulTmp)) << 2 | GetB64Index((char)B1(ulTmp)) << 2 >> 6) & 0xFF; + int b1 = (GetB64Index((char)B1(ulTmp)) << 4 | GetB64Index((char)B2(ulTmp)) << 2 >> 4) & 0xFF; + int b2 = (GetB64Index((char)B2(ulTmp)) << 6 | GetB64Index((char)B3(ulTmp)) << 2 >> 2) & 0xFF; + + *((unsigned long *)pbuf) = b0 | b1 << 8 | b2 << 16; + psrc += 4; + pbuf += 3; + len += 3; + } + + if (i < nSrcLen) + { + int rest = nSrcLen - i; + unsigned long ulTmp = 0; + for (int j = 0; j < rest; ++j) + { + *(((unsigned char *)&ulTmp) + j) = *psrc++; + } + + int b0 = (GetB64Index((char)B0(ulTmp)) << 2 | GetB64Index((char)B1(ulTmp)) << 2 >> 6) & 0xFF; + *pbuf++ = b0; + len++; + + if ('=' != B1(ulTmp) && '=' != B2(ulTmp)) + { + int b1 = (GetB64Index((char)B1(ulTmp)) << 4 | GetB64Index((char)B2(ulTmp)) << 2 >> 4) & 0xFF; + *pbuf++ = b1; + len++; + } + + if ('=' != B2(ulTmp) && '=' != B3(ulTmp)) + { + int b2 = (GetB64Index((char)B2(ulTmp)) << 6 | GetB64Index((char)B3(ulTmp)) << 2 >> 2) & 0xFF; + *pbuf++ = b2; + len++; + } + } + *pbuf = '\0'; + + if (NULL != pDecLen) + { + *pDecLen = (int)(pbuf - szDec); + } + + return szDec; +} + +const char *ZBase64::Decode(const char *szSrc, string &strOutput) +{ + strOutput.clear(); + int nDecLen = 0; + const char *p = Decode(szSrc, 0, &nDecLen); + strOutput.append(p, nDecLen); + return strOutput.data(); +} diff --git a/ZSign/common/base64.h b/ZSign/common/base64.h new file mode 100644 index 0000000..3b38dc1 --- /dev/null +++ b/ZSign/common/base64.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +using namespace std; + +class ZBase64 +{ +public: + ZBase64(void); + ~ZBase64(void); + +public: + const char *Encode(const char *szSrc, int nSrcLen = 0); + const char *Encode(const string &strInput); + const char *Decode(const char *szSrc, int nSrcLen = 0, int *pDecLen = NULL); + const char *Decode(const char *szSrc, string &strOutput); + +private: + inline int GetB64Index(char ch); + inline char GetB64char(int nIndex); + +private: + vector m_arrDec; + vector m_arrEnc; +}; diff --git a/ZSign/common/common.cpp b/ZSign/common/common.cpp new file mode 100644 index 0000000..92ad02d --- /dev/null +++ b/ZSign/common/common.cpp @@ -0,0 +1,849 @@ +#include "common.h" +#include "base64.h" +#include +#include +#include +#include +#include "../Utils.hpp" +#include + +#define PARSEVALIST(szFormatArgs, szArgs) \ + ZBuffer buffer; \ + char szBuffer[PATH_MAX] = {0}; \ + char *szArgs = szBuffer; \ + va_list args; \ + va_start(args, szFormatArgs); \ + int nRet = vsnprintf(szArgs, PATH_MAX, szFormatArgs, args); \ + va_end(args); \ + if (nRet > PATH_MAX - 1) \ + { \ + char *szNewBuffer = buffer.GetBuffer(nRet + 1); \ + if (NULL != szNewBuffer) \ + { \ + szArgs = szNewBuffer; \ + va_start(args, szFormatArgs); \ + vsnprintf(szArgs, nRet + 1, szFormatArgs, args); \ + va_end(args); \ + } \ + } + +bool IsRegularFile(const char *file) +{ + struct stat info; + stat(file, &info); + return S_ISREG(info.st_mode); +} + +void *MapFile(const char *path, size_t offset, size_t size, size_t *psize, bool ro) +{ + refreshFile(path); + int fd = open(path, ro ? O_RDONLY : O_RDWR); + if (fd <= 0) + { + return NULL; + } + + if (0 == size) + { + struct stat stat; + fstat(fd, &stat); + size = stat.st_size; + } + + if (NULL != psize) + { + *psize = size; + } + + void *base = mmap(NULL, size, ro ? PROT_READ : PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset); + close(fd); + + if (MAP_FAILED == base) + { + base = NULL; + } + + return base; +} + +bool WriteFile(const char *szFile, const char *szData, size_t sLen) +{ + if (NULL == szFile) + { + return false; + } + + FILE *fp = fopen(szFile, "wb"); + if (NULL != fp) + { + int64_t towrite = sLen; + if (NULL != szData) + { + while (towrite > 0) + { + int64_t nwrite = fwrite(szData + (sLen - towrite), 1, towrite, fp); + if (nwrite <= 0) + { + break; + } + towrite -= nwrite; + } + } + fclose(fp); + return (towrite > 0) ? false : true; + } + else + { + ZLog::ErrorV("WriteFile: Failed in fopen! %s, %s\n", szFile, strerror(errno)); + } + + return false; +} + +bool WriteFile(const char *szFile, const string &strData) +{ + return WriteFile(szFile, strData.data(), strData.size()); +} + +bool WriteFile(string &strData, const char *szFormatPath, ...) +{ + PARSEVALIST(szFormatPath, szPath) + return WriteFile(szPath, strData); +} + +bool WriteFile(const char *szData, size_t sLen, const char *szFormatPath, ...) +{ + PARSEVALIST(szFormatPath, szPath) + return WriteFile(szPath, szData, sLen); +} + +bool ReadFile(const char *szFile, string &strData) +{ + strData.clear(); + + if (!IsFileExists(szFile)) + { + return false; + } + + FILE *fp = fopen(szFile, "rb"); + if (NULL != fp) + { + strData.reserve(GetFileSize(fileno(fp))); + + char buf[4096] = {0}; + size_t nread = fread(buf, 1, 4096, fp); + while (nread > 0) + { + strData.append(buf, nread); + nread = fread(buf, 1, 4096, fp); + } + fclose(fp); + return true; + } + else + { + ZLog::ErrorV("ReadFile: Failed in fopen! %s, %s\n", szFile, strerror(errno)); + } + + return false; +} + +bool ReadFile(string &strData, const char *szFormatPath, ...) +{ + PARSEVALIST(szFormatPath, szPath) + return ReadFile(szPath, strData); +} + +bool AppendFile(const char *szFile, const char *szData, size_t sLen) +{ + FILE *fp = fopen(szFile, "ab+"); + if (NULL != fp) + { + int64_t towrite = sLen; + while (towrite > 0) + { + int64_t nwrite = fwrite(szData + (sLen - towrite), 1, towrite, fp); + if (nwrite <= 0) + { + break; + } + towrite -= nwrite; + } + + fclose(fp); + return (towrite > 0) ? false : true; + } + else + { + ZLog::ErrorV("AppendFile: Failed in fopen! %s, %s\n", szFile, strerror(errno)); + } + return false; +} + +bool AppendFile(const char *szFile, const string &strData) +{ + return AppendFile(szFile, strData.data(), strData.size()); +} + +bool IsFolder(const char *szFolder) +{ + struct stat st; + stat(szFolder, &st); + return S_ISDIR(st.st_mode); +} + +bool IsFolderV(const char *szFormatPath, ...) +{ + PARSEVALIST(szFormatPath, szFolder) + return IsFolder(szFolder); +} + +bool CreateFolder(const char *szFolder) +{ + if (!IsFolder(szFolder)) + { +#if defined(WINDOWS) + return (0 == mkdir(szFolder)); +#else + return (0 == mkdir(szFolder, 0755)); +#endif + } + return false; +} + +bool CreateFolderV(const char *szFormatPath, ...) +{ + PARSEVALIST(szFormatPath, szFolder) + return CreateFolder(szFolder); +} + +int RemoveFolderCallBack(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) +{ + int ret = remove(fpath); + if (ret) + { + perror(fpath); + } + return ret; +} + +bool RemoveFolder(const char *szFolder) +{ + if (!IsFolder(szFolder)) + { + RemoveFile(szFolder); + return true; + } + return nftw(szFolder, RemoveFolderCallBack, 64, FTW_DEPTH | FTW_PHYS); +} + +bool RemoveFolderV(const char *szFormatPath, ...) +{ + PARSEVALIST(szFormatPath, szFolder) + return RemoveFolder(szFolder); +} + +bool RemoveFile(const char *szFile) +{ + return (0 == remove(szFile)); +} + +bool RemoveFileV(const char *szFormatPath, ...) +{ + PARSEVALIST(szFormatPath, szFile); + return RemoveFile(szFile); +} + +bool IsFileExists(const char *szFile) +{ + if (NULL == szFile) + { + return false; + } + return (0 == access(szFile, F_OK)); +} + +bool IsFileExistsV(const char *szFormatPath, ...) +{ + PARSEVALIST(szFormatPath, szFile) + return IsFileExists(szFile); +} + +bool IsZipFile(const char *szFile) +{ + if (NULL != szFile && !IsFolder(szFile)) + { + FILE *fp = fopen(szFile, "rb"); + if (NULL != fp) + { + uint8_t buf[2] = {0}; + fread(buf, 1, 2, fp); + fclose(fp); + return (0 == memcmp("PK", buf, 2)); + } + } + return false; +} +#define PATH_BUFFER_LENGTH 1024 + +string GetCanonicalizePath(const char *szPath) +{ + string strPath = szPath; + if (!strPath.empty()) + { + if ('/' != szPath[0]) + { + char path[PATH_MAX] = {0}; + +#if defined(WINDOWS) + + if (NULL != _fullpath((char *)"./", path, PATH_BUFFER_LENGTH)) + { + strPath = path; + strPath += "/"; + strPath += szPath; + } +#else + if (NULL != realpath("./", path)) + { + strPath = path; + strPath += "/"; + strPath += szPath; + } +#endif + } + StringReplace(strPath, "/./", "/"); + } + return strPath; +} + +int64_t GetFileSize(int fd) +{ + int64_t nSize = 0; + struct stat stbuf; + if (0 == fstat(fd, &stbuf)) + { + if (S_ISREG(stbuf.st_mode)) + { + nSize = stbuf.st_size; + } + } + return (nSize < 0 ? 0 : nSize); +} + +int64_t GetFileSize(const char *szFile) +{ + int64_t nSize = 0; + int fd = open(szFile, O_RDONLY); + if (fd >= 0) + { + nSize = GetFileSize(fd); + close(fd); + } + return nSize; +} + +int64_t GetFileSizeV(const char *szFormatPath, ...) +{ + PARSEVALIST(szFormatPath, szFile) + return GetFileSize(szFile); +} + +string GetFileSizeString(const char *szFile) +{ + return FormatSize(GetFileSize(szFile), 1024); +} + +string FormatSize(int64_t size, int64_t base) +{ + double fsize = 0; + char ret[64] = {0}; + if (size > base * base * base * base) + { + fsize = (size * 1.0) / (base * base * base * base); + sprintf(ret, "%.2f TB", fsize); + } + else if (size > base * base * base) + { + fsize = (size * 1.0) / (base * base * base); + sprintf(ret, "%.2f GB", fsize); + } + else if (size > base * base) + { + fsize = (size * 1.0) / (base * base); + sprintf(ret, "%.2f MB", fsize); + } + else if (size > base) + { + fsize = (size * 1.0) / (base); + sprintf(ret, "%.2f KB", fsize); + } + else + { + sprintf(ret, "%" PRId64 " B", size); + } + return ret; +} + +bool IsPathSuffix(const string &strPath, const char *suffix) +{ + size_t nPos = strPath.rfind(suffix); + if (string::npos != nPos) + { + if (nPos == (strPath.size() - strlen(suffix))) + { + return true; + } + } + return false; +} + +time_t GetUnixStamp() +{ + time_t ustime = 0; + time(&ustime); + return ustime; +} + +uint64_t GetMicroSecond() +{ + struct timeval tv = {0}; + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000000 + tv.tv_usec; +} + +bool SystemExec(const char *szFormatCmd, ...) +{ + /*PARSEVALIST(szFormatCmd, szCmd) + + if (strlen(szCmd) <= 0) + { + return false; + } + + int status = system(szCmd); + + if (-1 == status) + { + ZLog::ErrorV("SystemExec: \"%s\", Error!\n", szCmd); + return false; + } + else + { +#if !defined(WINDOWS) + if (WIFEXITED(status)) + { + if (0 == WEXITSTATUS(status)) + { + return true; + } + else + { + ZLog::ErrorV("SystemExec: \"%s\", Failed! Exit-Status: %d\n", szCmd, WEXITSTATUS(status)); + return false; + } + } + else + { + return true; + } +#endif + } + */ + return false; +} + +uint16_t _Swap(uint16_t value) +{ + return ((value >> 8) & 0x00ff) | + ((value << 8) & 0xff00); +} + +uint32_t _Swap(uint32_t value) +{ + value = ((value >> 8) & 0x00ff00ff) | + ((value << 8) & 0xff00ff00); + value = ((value >> 16) & 0x0000ffff) | + ((value << 16) & 0xffff0000); + return value; +} + +uint64_t _Swap(uint64_t value) +{ + value = (value & 0x00000000ffffffffULL) << 32 | (value & 0xffffffff00000000ULL) >> 32; + value = (value & 0x0000ffff0000ffffULL) << 16 | (value & 0xffff0000ffff0000ULL) >> 16; + value = (value & 0x00ff00ff00ff00ffULL) << 8 | (value & 0xff00ff00ff00ff00ULL) >> 8; + return value; +} + +uint32_t ByteAlign(uint32_t uValue, uint32_t uAlign) +{ + return (uValue + (uAlign - uValue % uAlign)); +} + +const char *StringFormat(string &strFormat, const char *szFormatArgs, ...) +{ + PARSEVALIST(szFormatArgs, szFormat) + strFormat = szFormat; + return strFormat.c_str(); +} + +string &StringReplace(string &context, const string &from, const string &to) +{ + size_t lookHere = 0; + size_t foundHere; + while ((foundHere = context.find(from, lookHere)) != string::npos) + { + context.replace(foundHere, from.size(), to); + lookHere = foundHere + to.size(); + } + return context; +} + +void StringSplit(const string &src, const string &split, vector &dest) +{ + size_t oldPos = 0; + size_t newPos = src.find(split, oldPos); + while (newPos != string::npos) + { + dest.push_back(src.substr(oldPos, newPos - oldPos)); + oldPos = newPos + split.size(); + newPos = src.find(split, oldPos); + } + if (oldPos < src.size()) + { + dest.push_back(src.substr(oldPos)); + } +} + +bool SHA1Text(const string &strData, string &strOutput) +{ + string strSHASum; + SHASum(E_SHASUM_TYPE_1, strData, strSHASum); + + strOutput.clear(); + char buf[16] = {0}; + for (size_t i = 0; i < strSHASum.size(); i++) + { + sprintf(buf, "%02x", (uint8_t)strSHASum[i]); + strOutput += buf; + } + return (!strOutput.empty()); +} + +void PrintSHASum(const char *prefix, const uint8_t *hash, uint32_t size, const char *suffix) +{ + ZLog::PrintV("%s", prefix); + for (uint32_t i = 0; i < size; i++) + { + ZLog::PrintV("%02x", hash[i]); + } + ZLog::PrintV("%s", suffix); +} + +void PrintSHASum(const char *prefix, const string &strSHASum, const char *suffix) +{ + PrintSHASum(prefix, (const uint8_t *)strSHASum.data(), strSHASum.size(), suffix); +} + +void PrintDataSHASum(const char *prefix, int nSumType, const string &strData, const char *suffix) +{ + string strSHASum; + SHASum(nSumType, strData, strSHASum); + PrintSHASum(prefix, strSHASum, suffix); +} + +void PrintDataSHASum(const char *prefix, int nSumType, uint8_t *data, size_t size, const char *suffix) +{ + string strSHASum; + SHASum(nSumType, data, size, strSHASum); + PrintSHASum(prefix, strSHASum, suffix); +} + +bool SHASum(int nSumType, uint8_t *data, size_t size, string &strOutput) +{ + strOutput.clear(); + if (1 == nSumType) + { + uint8_t hash[20]; + memset(hash, 0, 20); + SHA1(data, size, hash); + strOutput.append((const char *)hash, 20); + } + else + { + uint8_t hash[32]; + memset(hash, 0, 32); + SHA256(data, size, hash); + strOutput.append((const char *)hash, 32); + } + return true; +} + +bool SHASum(int nSumType, const string &strData, string &strOutput) +{ + return SHASum(nSumType, (uint8_t *)strData.data(), strData.size(), strOutput); +} + +bool SHASum(const string &strData, string &strSHA1, string &strSHA256) +{ + SHASum(E_SHASUM_TYPE_1, strData, strSHA1); + SHASum(E_SHASUM_TYPE_256, strData, strSHA256); + return (!strSHA1.empty() && !strSHA256.empty()); +} + +bool SHASumFile(const char *szFile, string &strSHA1, string &strSHA256) +{ + size_t sSize = 0; + uint8_t *pBase = (uint8_t *)MapFile(szFile, 0, 0, &sSize, true); + + SHASum(E_SHASUM_TYPE_1, pBase, sSize, strSHA1); + SHASum(E_SHASUM_TYPE_256, pBase, sSize, strSHA256); + + if (NULL != pBase && sSize > 0) + { + munmap(pBase, sSize); + } + return (!strSHA1.empty() && !strSHA256.empty()); +} + +bool SHASumBase64(const string &strData, string &strSHA1Base64, string &strSHA256Base64) +{ + ZBase64 b64; + string strSHA1; + string strSHA256; + SHASum(strData, strSHA1, strSHA256); + strSHA1Base64 = b64.Encode(strSHA1); + strSHA256Base64 = b64.Encode(strSHA256); + return (!strSHA1Base64.empty() && !strSHA256Base64.empty()); +} + +bool SHASumBase64File(const char *szFile, string &strSHA1Base64, string &strSHA256Base64) +{ + ZBase64 b64; + string strSHA1; + string strSHA256; + SHASumFile(szFile, strSHA1, strSHA256); + strSHA1Base64 = b64.Encode(strSHA1); + strSHA256Base64 = b64.Encode(strSHA256); + return (!strSHA1Base64.empty() && !strSHA256Base64.empty()); +} + +ZBuffer::ZBuffer() +{ + m_pData = NULL; + m_uSize = 0; +} + +ZBuffer::~ZBuffer() +{ + Free(); +} + +char *ZBuffer::GetBuffer(uint32_t uSize) +{ + if (uSize <= m_uSize) + { + return m_pData; + } + + char *pData = (char *)realloc(m_pData, uSize); + if (NULL == pData) + { + Free(); + return NULL; + } + + m_pData = pData; + m_uSize = uSize; + return m_pData; +} + +void ZBuffer::Free() +{ + if (NULL != m_pData) + { + free(m_pData); + } + m_pData = NULL; + m_uSize = 0; +} + +ZTimer::ZTimer() +{ + Reset(); +} + +uint64_t ZTimer::Reset() +{ + m_uBeginTime = GetMicroSecond(); + return m_uBeginTime; +} + +uint64_t ZTimer::Print(const char *szFormatArgs, ...) +{ + PARSEVALIST(szFormatArgs, szFormat) + uint64_t uElapse = GetMicroSecond() - m_uBeginTime; + ZLog::PrintV("%s (%.03fs, %lluus)\n", szFormat, uElapse / 1000000.0, uElapse); + return Reset(); +} + +uint64_t ZTimer::PrintResult(bool bSuccess, const char *szFormatArgs, ...) +{ + PARSEVALIST(szFormatArgs, szFormat) + uint64_t uElapse = GetMicroSecond() - m_uBeginTime; + ZLog::PrintResultV(bSuccess, "%s (%.03fs, %lluus)\n", szFormat, uElapse / 1000000.0, uElapse); + return Reset(); +} + +int ZLog::g_nLogLevel = ZLog::E_INFO; +vector ZLog::logs; + +void ZLog::SetLogLever(int nLogLevel) +{ + g_nLogLevel = nLogLevel; +} + +void ZLog::writeToLogFile(const std::string& message) { +// const char* documentsPath = getDocumentsDirectory(); +// std::string logFilePath = std::string(documentsPath) + "/logs.txt"; +// +// std::ofstream logFile(logFilePath, std::ios_base::app); +// if (logFile.is_open()) { +// logFile << message; +// logFile.close(); +// } else { +// std::cerr << "Failed to open log file: " << logFilePath << std::endl; +// } + writeToNSLog(message.data()); + logs.push_back(message); +} + +void ZLog::Print(int nLevel, const char *szLog) +{ + if (g_nLogLevel >= nLevel) + { + write(STDOUT_FILENO, szLog, strlen(szLog)); + writeToLogFile(szLog); + } +} + +void ZLog::PrintV(int nLevel, const char *szFormatArgs, ...) { + if (g_nLogLevel >= nLevel) { + PARSEVALIST(szFormatArgs, szLog) + write(STDOUT_FILENO, szLog, strlen(szLog)); + writeToLogFile(szLog); + } +} + +bool ZLog::Error(const char *szLog) +{ + write(STDOUT_FILENO, "\033[31m", 5); + write(STDOUT_FILENO, szLog, strlen(szLog)); + write(STDOUT_FILENO, "\033[0m", 4); + writeToLogFile(szLog); + return false; +} + +bool ZLog::ErrorV(const char *szFormatArgs, ...) +{ + PARSEVALIST(szFormatArgs, szLog) + write(STDOUT_FILENO, "\033[31m", 5); + write(STDOUT_FILENO, szLog, strlen(szLog)); + write(STDOUT_FILENO, "\033[0m", 4); + writeToLogFile(szLog); + return false; +} + +bool ZLog::Success(const char *szLog) +{ + write(STDOUT_FILENO, "\033[32m", 5); + write(STDOUT_FILENO, szLog, strlen(szLog)); + write(STDOUT_FILENO, "\033[0m", 4); + writeToLogFile(szLog); + return true; +} + +bool ZLog::SuccessV(const char *szFormatArgs, ...) +{ + PARSEVALIST(szFormatArgs, szLog) + write(STDOUT_FILENO, "\033[32m", 5); + write(STDOUT_FILENO, szLog, strlen(szLog)); + write(STDOUT_FILENO, "\033[0m", 4); + writeToLogFile(szLog); + return true; +} + +bool ZLog::PrintResult(bool bSuccess, const char *szLog) +{ + return bSuccess ? Success(szLog) : Error(szLog); +} + +bool ZLog::PrintResultV(bool bSuccess, const char *szFormatArgs, ...) +{ + PARSEVALIST(szFormatArgs, szLog) + return bSuccess ? Success(szLog) : Error(szLog); +} + +bool ZLog::Warn(const char *szLog) +{ + write(STDOUT_FILENO, "\033[33m", 5); + write(STDOUT_FILENO, szLog, strlen(szLog)); + write(STDOUT_FILENO, "\033[0m", 4); + writeToLogFile(szLog); + return false; +} + +bool ZLog::WarnV(const char *szFormatArgs, ...) +{ + PARSEVALIST(szFormatArgs, szLog) + write(STDOUT_FILENO, "\033[33m", 5); + write(STDOUT_FILENO, szLog, strlen(szLog)); + write(STDOUT_FILENO, "\033[0m", 4); + writeToLogFile(szLog); + return false; +} + +void ZLog::Print(const char *szLog) +{ + if (g_nLogLevel >= E_INFO) + { + write(STDOUT_FILENO, szLog, strlen(szLog)); + writeToLogFile(szLog); + } +} + +void ZLog::PrintV(const char *szFormatArgs, ...) +{ + if (g_nLogLevel >= E_INFO) + { + PARSEVALIST(szFormatArgs, szLog) + write(STDOUT_FILENO, szLog, strlen(szLog)); + writeToLogFile(szLog); + } +} + +void ZLog::Debug(const char *szLog) +{ + if (g_nLogLevel >= E_DEBUG) + { + write(STDOUT_FILENO, szLog, strlen(szLog)); + writeToLogFile(szLog); + } +} + +void ZLog::DebugV(const char *szFormatArgs, ...) +{ + if (g_nLogLevel >= E_DEBUG) + { + PARSEVALIST(szFormatArgs, szLog) + write(STDOUT_FILENO, szLog, strlen(szLog)); + writeToLogFile(szLog); + } +} + +bool ZLog::IsDebug() +{ + return (E_DEBUG == g_nLogLevel); +} diff --git a/ZSign/common/common.h b/ZSign/common/common.h new file mode 100644 index 0000000..d84a2e4 --- /dev/null +++ b/ZSign/common/common.h @@ -0,0 +1,163 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +using namespace std; + +#define LE(x) _Swap(x) +#define BE(x) _Swap(x) + +#if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG) +#define S_ISREG(m) (((m)&S_IFMT) == S_IFREG) +#endif + +uint16_t _Swap(uint16_t value); +uint32_t _Swap(uint32_t value); +uint64_t _Swap(uint64_t value); + +bool ReadFile(const char *szFile, string &strData); +bool ReadFile(string &strData, const char *szFormatPath, ...); +bool WriteFile(const char *szFile, const string &strData); +bool WriteFile(const char *szFile, const char *szData, size_t sLen); +bool WriteFile(string &strData, const char *szFormatPath, ...); +bool WriteFile(const char *szData, size_t sLen, const char *szFormatPath, ...); +bool AppendFile(const char *szFile, const string &strData); +bool AppendFile(const char *szFile, const char *szData, size_t sLen); +bool AppendFile(const string &strData, const char *szFormatPath, ...); +bool IsRegularFile(const char *szFile); +bool IsFolder(const char *szFolder); +bool IsFolderV(const char *szFormatPath, ...); +bool CreateFolder(const char *szFolder); +bool CreateFolderV(const char *szFormatPath, ...); +bool RemoveFile(const char *szFile); +bool RemoveFileV(const char *szFormatPath, ...); +bool RemoveFolder(const char *szFolder); +bool RemoveFolderV(const char *szFormatPath, ...); +bool IsFileExists(const char *szFile); +bool IsFileExistsV(const char *szFormatPath, ...); +int64_t GetFileSize(int fd); +int64_t GetFileSize(const char *szFile); +int64_t GetFileSizeV(const char *szFormatPath, ...); +string GetFileSizeString(const char *szFile); +bool IsZipFile(const char *szFile); +string GetCanonicalizePath(const char *szPath); +void *MapFile(const char *path, size_t offset, size_t size, size_t *psize, bool ro); +bool IsPathSuffix(const string &strPath, const char *suffix); + +const char *StringFormat(string &strFormat, const char *szFormatArgs, ...); +string &StringReplace(string &context, const string &from, const string &to); +void StringSplit(const string &src, const string &split, vector &dest); + +string FormatSize(int64_t size, int64_t base = 1024); +time_t GetUnixStamp(); +uint64_t GetMicroSecond(); +bool SystemExec(const char *szFormatCmd, ...); +uint32_t ByteAlign(uint32_t uValue, uint32_t uAlign); + +enum +{ + E_SHASUM_TYPE_1 = 1, + E_SHASUM_TYPE_256 = 2, +}; + +bool SHASum(int nSumType, uint8_t *data, size_t size, string &strOutput); +bool SHASum(int nSumType, const string &strData, string &strOutput); +bool SHASum(const string &strData, string &strSHA1, string &strSHA256); +bool SHA1Text(const string &strData, string &strOutput); +bool SHASumFile(const char *szFile, string &strSHA1, string &strSHA256); +bool SHASumBase64(const string &strData, string &strSHA1Base64, string &strSHA256Base64); +bool SHASumBase64File(const char *szFile, string &strSHA1Base64, string &strSHA256Base64); +void PrintSHASum(const char *prefix, const uint8_t *hash, uint32_t size, const char *suffix = "\n"); +void PrintSHASum(const char *prefix, const string &strSHASum, const char *suffix = "\n"); +void PrintDataSHASum(const char *prefix, int nSumType, const string &strData, const char *suffix = "\n"); +void PrintDataSHASum(const char *prefix, int nSumType, uint8_t *data, size_t size, const char *suffix = "\n"); + +class ZBuffer +{ +public: + ZBuffer(); + ~ZBuffer(); + +public: + char *GetBuffer(uint32_t uSize); + +private: + void Free(); + +private: + char *m_pData; + uint32_t m_uSize; +}; + +class ZTimer +{ +public: + ZTimer(); + +public: + uint64_t Reset(); + uint64_t Print(const char *szFormatArgs, ...); + uint64_t PrintResult(bool bSuccess, const char *szFormatArgs, ...); + +private: + uint64_t m_uBeginTime; +}; + +class ZLog +{ +public: + enum eLogType + { + E_NONE = 0, + E_ERROR = 1, + E_WARN = 2, + E_INFO = 3, + E_DEBUG = 4 + }; + +public: + static bool IsDebug(); + static void Print(const char *szLog); + static void PrintV(const char *szFormatArgs, ...); + static void Debug(const char *szLog); + static void DebugV(const char *szFormatArgs, ...); + static bool Warn(const char *szLog); + static bool WarnV(const char *szFormatArgs, ...); + static bool Error(const char *szLog); + static bool ErrorV(const char *szFormatArgs, ...); + static bool Success(const char *szLog); + static bool SuccessV(const char *szFormatArgs, ...); + static bool PrintResult(bool bSuccess, const char *szLog); + static bool PrintResultV(bool bSuccess, const char *szFormatArgs, ...); + static void Print(int nLevel, const char *szLog); + static void PrintV(int nLevel, const char *szFormatArgs, ...); + static void SetLogLever(int nLogLevel); + static vector logs; + +private: + static int g_nLogLevel; + static void writeToLogFile(const std::string& message); + +}; diff --git a/ZSign/common/json.cpp b/ZSign/common/json.cpp new file mode 100644 index 0000000..99721bb --- /dev/null +++ b/ZSign/common/json.cpp @@ -0,0 +1,3061 @@ +#include "json.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "base64.h" + +#ifndef WIN32 +#define _atoi64(val) strtoll(val, NULL, 10) +#endif + +const JValue JValue::null; +const string JValue::nullData; + +JValue::JValue(TYPE type) : m_eType(type) +{ + m_Value.vFloat = 0; +} + +JValue::JValue(int val) : m_eType(E_INT) +{ + m_Value.vInt64 = val; +} + +JValue::JValue(int64_t val) : m_eType(E_INT) +{ + m_Value.vInt64 = val; +} + +JValue::JValue(bool val) : m_eType(E_BOOL) +{ + m_Value.vBool = val; +} + +JValue::JValue(double val) : m_eType(E_FLOAT) +{ + m_Value.vFloat = val; +} + +JValue::JValue(const char *val) : m_eType(E_STRING) +{ + m_Value.vString = NewString(val); +} + +JValue::JValue(const string &val) : m_eType(E_STRING) +{ + m_Value.vString = NewString(val.c_str()); +} + +JValue::JValue(const JValue &other) +{ + CopyValue(other); +} + +JValue::JValue(const char *val, size_t len) : m_eType(E_DATA) +{ + m_Value.vData = new string(); + m_Value.vData->append(val, len); +} + +JValue::~JValue() +{ + Free(); +} + +void JValue::clear() +{ + Free(); +} + +bool JValue::isInt() const +{ + return (E_INT == m_eType); +} + +bool JValue::isNull() const +{ + return (E_NULL == m_eType); +} + +bool JValue::isBool() const +{ + return (E_BOOL == m_eType); +} + +bool JValue::isFloat() const +{ + return (E_FLOAT == m_eType); +} + +bool JValue::isString() const +{ + return (E_STRING == m_eType); +} + +bool JValue::isArray() const +{ + return (E_ARRAY == m_eType); +} + +bool JValue::isObject() const +{ + return (E_OBJECT == m_eType); +} + +bool JValue::isEmpty() const +{ + switch (m_eType) + { + case E_NULL: + return true; + break; + case E_INT: + return (0 == m_Value.vInt64); + break; + case E_BOOL: + return (false == m_Value.vBool); + break; + case E_FLOAT: + return (0 == m_Value.vFloat); + break; + case E_ARRAY: + case E_OBJECT: + return (0 == size()); + break; + case E_STRING: + return (0 == strlen(asCString())); + case E_DATE: + return (0 == m_Value.vDate); + break; + case E_DATA: + return (NULL == m_Value.vData) ? true : m_Value.vData->empty(); + break; + } + return true; +} + +JValue::operator const char *() const +{ + return asCString(); +} + +JValue::operator int() const +{ + return asInt(); +} + +JValue::operator int64_t() const +{ + return asInt64(); +} + +JValue::operator double() const +{ + return asFloat(); +} + +JValue::operator string() const +{ + return asCString(); +} + +JValue::operator bool() const +{ + return asBool(); +} + +char *JValue::NewString(const char *cstr) +{ + char *str = NULL; + if (NULL != cstr) + { + size_t len = (strlen(cstr) + 1) * sizeof(char); + str = (char *)malloc(len); + memcpy(str, cstr, len); + } + return str; +} + +void JValue::CopyValue(const JValue &src) +{ + m_eType = src.m_eType; + switch (m_eType) + { + case E_ARRAY: + m_Value.vArray = (NULL == src.m_Value.vArray) ? NULL : new vector(*(src.m_Value.vArray)); + break; + case E_OBJECT: + m_Value.vObject = (NULL == src.m_Value.vObject) ? NULL : new map(*(src.m_Value.vObject)); + break; + case E_STRING: + m_Value.vString = (NULL == src.m_Value.vString) ? NULL : NewString(src.m_Value.vString); + break; + case E_DATA: + { + if (NULL != src.m_Value.vData) + { + m_Value.vData = new string(); + *m_Value.vData = *src.m_Value.vData; + } + else + { + m_Value.vData = NULL; + } + } + break; + default: + m_Value = src.m_Value; + break; + } +} + +void JValue::Free() +{ + switch (m_eType) + { + case E_INT: + { + m_Value.vInt64 = 0; + } + break; + case E_BOOL: + { + m_Value.vBool = false; + } + break; + case E_FLOAT: + { + m_Value.vFloat = 0.0; + } + break; + case E_STRING: + { + if (NULL != m_Value.vString) + { + free(m_Value.vString); + m_Value.vString = NULL; + } + } + break; + case E_ARRAY: + { + if (NULL != m_Value.vArray) + { + delete m_Value.vArray; + m_Value.vArray = NULL; + } + } + break; + case E_OBJECT: + { + if (NULL != m_Value.vObject) + { + delete m_Value.vObject; + m_Value.vObject = NULL; + } + } + break; + case E_DATE: + { + m_Value.vDate = 0; + } + break; + case E_DATA: + { + if (NULL != m_Value.vData) + { + delete m_Value.vData; + m_Value.vData = NULL; + } + } + break; + default: + break; + } + m_eType = E_NULL; +} + +JValue &JValue::operator=(const JValue &other) +{ + if (this != &other) + { + Free(); + CopyValue(other); + } + return (*this); +} + +JValue::TYPE JValue::type() const +{ + return m_eType; +} + +int JValue::asInt() const +{ + return (int)asInt64(); +} + +int64_t JValue::asInt64() const +{ + switch (m_eType) + { + case E_INT: + return m_Value.vInt64; + break; + case E_BOOL: + return m_Value.vBool ? 1 : 0; + break; + case E_FLOAT: + return int(m_Value.vFloat); + break; + case E_STRING: + return _atoi64(asCString()); + break; + default: + break; + } + return 0; +} + +double JValue::asFloat() const +{ + switch (m_eType) + { + case E_INT: + return double(m_Value.vInt64); + break; + case E_BOOL: + return m_Value.vBool ? 1.0 : 0.0; + break; + case E_FLOAT: + return m_Value.vFloat; + break; + case E_STRING: + return atof(asCString()); + break; + default: + break; + } + return 0.0; +} + +bool JValue::asBool() const +{ + switch (m_eType) + { + case E_BOOL: + return m_Value.vBool; + break; + case E_INT: + return (0 != m_Value.vInt64); + break; + case E_FLOAT: + return (0.0 != m_Value.vFloat); + break; + case E_ARRAY: + return (NULL == m_Value.vArray) ? false : (m_Value.vArray->size() > 0); + break; + case E_OBJECT: + return (NULL == m_Value.vObject) ? false : (m_Value.vObject->size() > 0); + break; + case E_STRING: + return (NULL == m_Value.vString) ? false : (strlen(m_Value.vString) > 0); + break; + case E_DATE: + return (m_Value.vDate > 0); + break; + case E_DATA: + return (NULL == m_Value.vData) ? false : (m_Value.vData->size() > 0); + break; + default: + break; + } + return false; +} + +string JValue::asString() const +{ + switch (m_eType) + { + case E_BOOL: + return m_Value.vBool ? "true" : "false"; + break; + case E_INT: + { + char buf[256]; + sprintf(buf, "%" PRId64, m_Value.vInt64); + return buf; + } + break; + case E_FLOAT: + { + char buf[256]; + sprintf(buf, "%lf", m_Value.vFloat); + return buf; + } + break; + case E_ARRAY: + return "array"; + break; + case E_OBJECT: + return "object"; + break; + case E_STRING: + return (NULL == m_Value.vString) ? "" : m_Value.vString; + break; + case E_DATE: + return "date"; + break; + case E_DATA: + return "data"; + break; + default: + break; + } + return ""; +} + +const char *JValue::asCString() const +{ + if (E_STRING == m_eType && NULL != m_Value.vString) + { + return m_Value.vString; + } + return ""; +} + +size_t JValue::size() const +{ + switch (m_eType) + { + case E_ARRAY: + return (NULL == m_Value.vArray) ? 0 : m_Value.vArray->size(); + break; + case E_OBJECT: + return (NULL == m_Value.vObject) ? 0 : m_Value.vObject->size(); + break; + case E_DATA: + return (NULL == m_Value.vData) ? 0 : m_Value.vData->size(); + break; + default: + break; + } + return 0; +} + +JValue &JValue::operator[](int index) +{ + return (*this)[(size_t)(index < 0 ? 0 : index)]; +} + +const JValue &JValue::operator[](int index) const +{ + return (*this)[(size_t)(index < 0 ? 0 : index)]; +} + +JValue &JValue::operator[](int64_t index) +{ + return (*this)[(size_t)(index < 0 ? 0 : index)]; +} + +const JValue &JValue::operator[](int64_t index) const +{ + return (*this)[(size_t)(index < 0 ? 0 : index)]; +} + +JValue &JValue::operator[](size_t index) +{ + if (E_ARRAY != m_eType || NULL == m_Value.vArray) + { + Free(); + m_eType = E_ARRAY; + m_Value.vArray = new vector(); + } + + size_t sum = m_Value.vArray->size(); + if (sum <= index) + { + size_t fill = index - sum; + for (size_t i = 0; i <= fill; i++) + { + m_Value.vArray->push_back(null); + } + } + + return m_Value.vArray->at(index); +} + +const JValue &JValue::operator[](size_t index) const +{ + if (E_ARRAY == m_eType && NULL != m_Value.vArray) + { + if (index < m_Value.vArray->size()) + { + return m_Value.vArray->at(index); + } + } + return null; +} + +JValue &JValue::operator[](const string &key) +{ + return (*this)[key.c_str()]; +} + +const JValue &JValue::operator[](const string &key) const +{ + return (*this)[key.c_str()]; +} + +JValue &JValue::operator[](const char *key) +{ + map::iterator it; + if (E_OBJECT != m_eType || NULL == m_Value.vObject) + { + Free(); + m_eType = E_OBJECT; + m_Value.vObject = new map(); + } + else + { + it = m_Value.vObject->find(key); + if (it != m_Value.vObject->end()) + { + return it->second; + } + } + it = m_Value.vObject->insert(m_Value.vObject->end(), make_pair(key, null)); + return it->second; +} + +const JValue &JValue::operator[](const char *key) const +{ + if (E_OBJECT == m_eType && NULL != m_Value.vObject) + { + map::const_iterator it = m_Value.vObject->find(key); + if (it != m_Value.vObject->end()) + { + return it->second; + } + } + return null; +} + +bool JValue::has(const char *key) const +{ + if (E_OBJECT == m_eType && NULL != m_Value.vObject) + { + if (m_Value.vObject->end() != m_Value.vObject->find(key)) + { + return true; + } + } + + return false; +} + +int JValue::index(const char *ele) const +{ + if (E_ARRAY == m_eType && NULL != m_Value.vArray) + { + for (size_t i = 0; i < m_Value.vArray->size(); i++) + { + if (ele == (*m_Value.vArray)[i].asString()) + { + return (int)i; + } + } + } + + return -1; +} + +JValue &JValue::at(int index) +{ + return (*this)[index]; +} + +JValue &JValue::at(size_t index) +{ + return (*this)[index]; +} + +JValue &JValue::at(const char *key) +{ + return (*this)[key]; +} + +bool JValue::remove(int index) +{ + if (index >= 0) + { + return remove((size_t)index); + } + return false; +} + +bool JValue::remove(size_t index) +{ + if (E_ARRAY == m_eType && NULL != m_Value.vArray) + { + if (index < m_Value.vArray->size()) + { + m_Value.vArray->erase(m_Value.vArray->begin() + index); + return true; + } + } + return false; +} + +bool JValue::remove(const char *key) +{ + if (E_OBJECT == m_eType && NULL != m_Value.vObject) + { + if (m_Value.vObject->end() != m_Value.vObject->find(key)) + { + m_Value.vObject->erase(key); + return !has(key); + } + } + return false; +} + +bool JValue::keys(vector &arrKeys) const +{ + if (E_OBJECT == m_eType && NULL != m_Value.vObject) + { + arrKeys.reserve(m_Value.vObject->size()); + map::iterator itbeg = m_Value.vObject->begin(); + map::iterator itend = m_Value.vObject->end(); + for (; itbeg != itend; itbeg++) + { + arrKeys.push_back((itbeg->first).c_str()); + } + return true; + } + return false; +} + +string JValue::write() const +{ + string strDoc; + return write(strDoc); +} + +const char *JValue::write(string &strDoc) const +{ + strDoc.clear(); + JWriter::FastWrite((*this), strDoc); + return strDoc.c_str(); +} + +bool JValue::read(const string &strdoc, string *pstrerr) +{ + return read(strdoc.c_str(), pstrerr); +} + +bool JValue::read(const char *pdoc, string *pstrerr) +{ + JReader reader; + bool bret = reader.parse(pdoc, *this); + if (!bret) + { + if (NULL != pstrerr) + { + reader.error(*pstrerr); + } + } + return bret; +} + +JValue &JValue::front() +{ + if (E_ARRAY == m_eType) + { + if (size() > 0) + { + return *(m_Value.vArray->begin()); + } + } + else if (E_OBJECT == m_eType) + { + if (size() > 0) + { + return m_Value.vObject->begin()->second; + } + } + return (*this); +} + +JValue &JValue::back() +{ + if (E_ARRAY == m_eType) + { + if (size() > 0) + { + return *(m_Value.vArray->rbegin()); + } + } + else if (E_OBJECT == m_eType) + { + if (size() > 0) + { + return m_Value.vObject->rbegin()->second; + } + } + return (*this); +} + +bool JValue::join(JValue &jv) +{ + if ((E_OBJECT == m_eType || E_NULL == m_eType) && E_OBJECT == jv.type()) + { + vector arrKeys; + jv.keys(arrKeys); + for (size_t i = 0; i < arrKeys.size(); i++) + { + (*this)[arrKeys[i]] = jv[arrKeys[i]]; + } + return true; + } + else if ((E_ARRAY == m_eType || E_NULL == m_eType) && E_ARRAY == jv.type()) + { + size_t count = this->size(); + for (size_t i = 0; i < jv.size(); i++) + { + (*this)[count] = jv[i]; + count++; + } + return true; + } + + return false; +} + +bool JValue::append(JValue &jv) +{ + if (E_ARRAY == m_eType || E_NULL == m_eType) + { + (*this)[((this->size() > 0) ? this->size() : 0)] = jv; + return true; + } + return false; +} + +bool JValue::push_back(int val) +{ + return push_back(JValue(val)); +} + +bool JValue::push_back(bool val) +{ + return push_back(JValue(val)); +} + +bool JValue::push_back(double val) +{ + return push_back(JValue(val)); +} + +bool JValue::push_back(int64_t val) +{ + return push_back(JValue(val)); +} + +bool JValue::push_back(const char *val) +{ + return push_back(JValue(val)); +} + +bool JValue::push_back(const string &val) +{ + return push_back(JValue(val)); +} + +bool JValue::push_back(const JValue &jval) +{ + if (E_ARRAY == m_eType || E_NULL == m_eType) + { + (*this)[size()] = jval; + return true; + } + return false; +} + +bool JValue::push_back(const char *val, size_t len) +{ + return push_back(JValue(val, len)); +} + +std::string JValue::styleWrite() const +{ + string strDoc; + return styleWrite(strDoc); +} + +const char *JValue::styleWrite(string &strDoc) const +{ + strDoc.clear(); + JWriter jw; + strDoc = jw.StyleWrite(*this); + return strDoc.c_str(); +} + +void JValue::assignDate(time_t val) +{ + Free(); + m_eType = E_DATE; + m_Value.vDate = val; +} + +void JValue::assignData(const char *val, size_t size) +{ + Free(); + m_eType = E_DATA; + m_Value.vData = new string(); + m_Value.vData->append(val, size); +} + +void JValue::assignDateString(time_t val) +{ + Free(); + m_eType = E_STRING; + m_Value.vString = NewString(JWriter::d2s(val).c_str()); +} + +time_t JValue::asDate() const +{ + switch (m_eType) + { + case E_DATE: + return m_Value.vDate; + break; + case E_STRING: + { + if (isDateString()) + { + tm ft = {0}; + sscanf(m_Value.vString + 5, "%04d-%02d-%02dT%02d:%02d:%02dZ", &ft.tm_year, &ft.tm_mon, &ft.tm_mday, &ft.tm_hour, &ft.tm_min, &ft.tm_sec); + ft.tm_mon -= 1; + ft.tm_year -= 1900; + return mktime(&ft); + } + } + break; + default: + break; + } + return 0; +} + +string JValue::asData() const +{ + switch (m_eType) + { + case E_DATA: + return (NULL == m_Value.vData) ? nullData : *m_Value.vData; + break; + case E_STRING: + { + if (isDataString()) + { + ZBase64 b64; + int nDataLen = 0; + const char *pdata = b64.Decode(m_Value.vString + 5, 0, &nDataLen); + string strdata; + strdata.append(pdata, nDataLen); + return strdata; + } + } + break; + default: + break; + } + + return nullData; +} + +bool JValue::isData() const +{ + return (E_DATA == m_eType); +} + +bool JValue::isDate() const +{ + return (E_DATE == m_eType); +} + +bool JValue::isDataString() const +{ + if (E_STRING == m_eType) + { + if (NULL != m_Value.vString) + { + if (strlen(m_Value.vString) >= 5) + { + if (0 == memcmp(m_Value.vString, "data:", 5)) + { + return true; + } + } + } + } + + return false; +} + +bool JValue::isDateString() const +{ + if (E_STRING == m_eType) + { + if (NULL != m_Value.vString) + { + if (25 == strlen(m_Value.vString)) + { + if (0 == memcmp(m_Value.vString, "date:", 5)) + { + const char *pdate = m_Value.vString + 5; + if ('T' == pdate[10] && 'Z' == pdate[19]) + { + return true; + } + } + } + } + } + + return false; +} + +bool JValue::readPList(const string &strdoc, string *pstrerr /*= NULL*/) +{ + return readPList(strdoc.data(), strdoc.size(), pstrerr); +} + +bool JValue::readPList(const char *pdoc, size_t len /*= 0*/, string *pstrerr /*= NULL*/) +{ + if (NULL == pdoc) + { + return false; + } + + if (0 == len) + { + len = strlen(pdoc); + } + + PReader reader; + bool bret = reader.parse(pdoc, len, *this); + if (!bret) + { + if (NULL != pstrerr) + { + reader.error(*pstrerr); + } + } + + return bret; +} + +bool JValue::readFile(const char *file, string *pstrerr /*= NULL*/) +{ + if (NULL != file) + { + FILE *fp = fopen(file, "rb"); + if (NULL != fp) + { + string strdata; + struct stat stbuf; + if (0 == fstat(fileno(fp), &stbuf)) + { + if (S_ISREG(stbuf.st_mode)) + { + strdata.reserve(stbuf.st_size); + } + } + + char buf[4096] = {0}; + int nread = (int)fread(buf, 1, 4096, fp); + while (nread > 0) + { + strdata.append(buf, nread); + nread = (int)fread(buf, 1, 4096, fp); + } + fclose(fp); + return read(strdata, pstrerr); + } + } + + return false; +} + +bool JValue::readPListFile(const char *file, string *pstrerr /*= NULL*/) +{ + if (NULL != file) + { + FILE *fp = fopen(file, "rb"); + if (NULL != fp) + { + string strdata; + struct stat stbuf; + if (0 == fstat(fileno(fp), &stbuf)) + { + if (S_ISREG(stbuf.st_mode)) + { + strdata.reserve(stbuf.st_size); + } + } + + char buf[4096] = {0}; + int nread = (int)fread(buf, 1, 4096, fp); + while (nread > 0) + { + strdata.append(buf, nread); + nread = (int)fread(buf, 1, 4096, fp); + } + fclose(fp); + return readPList(strdata, pstrerr); + } + } + + return false; +} + +bool JValue::WriteDataToFile(const char *file, const char *data, size_t len) +{ + if (NULL == file || NULL == data || len <= 0) + { + return false; + } + + FILE *fp = fopen(file, "wb"); + if (NULL != fp) + { + int towrite = (int)len; + while (towrite > 0) + { + int nwrite = (int)fwrite(data + (len - towrite), 1, towrite, fp); + if (nwrite <= 0) + { + break; + } + towrite -= nwrite; + } + + fclose(fp); + return (towrite > 0) ? false : true; + } + + return false; +} + +bool JValue::writeFile(const char *file) +{ + string strdata; + write(strdata); + return WriteDataToFile(file, strdata.data(), strdata.size()); +} + +bool JValue::writePListFile(const char *file) +{ + string strdata; + writePList(strdata); + return WriteDataToFile(file, strdata.data(), strdata.size()); +} + +bool JValue::styleWriteFile(const char *file) +{ + string strdata; + styleWrite(strdata); + return WriteDataToFile(file, strdata.data(), strdata.size()); +} + +bool JValue::readPath(const char *path, ...) +{ + char file[1024] = {0}; + va_list args; + va_start(args, path); + vsnprintf(file, 1024, path, args); + va_end(args); + + return readFile(file); +} + +bool JValue::readPListPath(const char *path, ...) +{ + char file[1024] = {0}; + va_list args; + va_start(args, path); + vsnprintf(file, 1024, path, args); + va_end(args); + + return readPListFile(file); +} + +bool JValue::writePath(const char *path, ...) +{ + char file[1024] = {0}; + va_list args; + va_start(args, path); + vsnprintf(file, 1024, path, args); + va_end(args); + + return writeFile(file); +} + +bool JValue::writePListPath(const char *path, ...) +{ + char file[1024] = {0}; + va_list args; + va_start(args, path); + vsnprintf(file, 1024, path, args); + va_end(args); + + return writePListFile(file); +} + +bool JValue::styleWritePath(const char *path, ...) +{ + char file[1024] = {0}; + va_list args; + va_start(args, path); + vsnprintf(file, 1024, path, args); + va_end(args); + + return styleWriteFile(file); +} + +string JValue::writePList() const +{ + string strDoc; + return writePList(strDoc); +} + +const char *JValue::writePList(string &strDoc) const +{ + strDoc.clear(); + PWriter::FastWrite((*this), strDoc); + return strDoc.c_str(); +} + +// Class Reader +// ////////////////////////////////////////////////////////////////// +bool JReader::parse(const char *pdoc, JValue &root) +{ + root.clear(); + if (NULL != pdoc) + { + m_pBeg = pdoc; + m_pEnd = m_pBeg + strlen(pdoc); + m_pCur = m_pBeg; + m_pErr = m_pBeg; + m_strErr = "null"; + return readValue(root); + } + return false; +} + +bool JReader::readValue(JValue &jval) +{ + Token token; + readToken(token); + switch (token.type) + { + case Token::E_True: + jval = true; + break; + case Token::E_False: + jval = false; + break; + case Token::E_Null: + jval = JValue(); + break; + case Token::E_Number: + return decodeNumber(token, jval); + break; + case Token::E_ArrayBegin: + return readArray(jval); + break; + case Token::E_ObjectBegin: + return readObject(jval); + break; + case Token::E_String: + { + string strval; + bool bok = decodeString(token, strval); + if (bok) + { + jval = strval.c_str(); + } + return bok; + } + break; + default: + return addError("Syntax error: value, object or array expected.", token.pbeg); + break; + } + return true; +} + +bool JReader::readToken(Token &token) +{ + skipSpaces(); + token.pbeg = m_pCur; + switch (GetNextChar()) + { + case '{': + token.type = Token::E_ObjectBegin; + break; + case '}': + token.type = Token::E_ObjectEnd; + break; + case '[': + token.type = Token::E_ArrayBegin; + break; + case ']': + token.type = Token::E_ArrayEnd; + break; + case ',': + token.type = Token::E_ArraySeparator; + break; + case ':': + token.type = Token::E_MemberSeparator; + break; + case 0: + token.type = Token::E_End; + break; + case '"': + token.type = readString() ? Token::E_String : Token::E_Error; + break; + case '/': + case '#': + case ';': + { + skipComment(); + return readToken(token); + } + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + { + token.type = Token::E_Number; + readNumber(); + } + break; + case 't': + token.type = match("rue", 3) ? Token::E_True : Token::E_Error; + break; + case 'f': + token.type = match("alse", 4) ? Token::E_False : Token::E_Error; + break; + case 'n': + token.type = match("ull", 3) ? Token::E_Null : Token::E_Error; + break; + default: + token.type = Token::E_Error; + break; + } + token.pend = m_pCur; + return true; +} + +void JReader::skipSpaces() +{ + while (m_pCur != m_pEnd) + { + char c = *m_pCur; + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + { + m_pCur++; + } + else + { + break; + } + } +} + +bool JReader::match(const char *pattern, int patternLength) +{ + if (m_pEnd - m_pCur < patternLength) + { + return false; + } + int index = patternLength; + while (index--) + { + if (m_pCur[index] != pattern[index]) + { + return false; + } + } + m_pCur += patternLength; + return true; +} + +void JReader::skipComment() +{ + char c = GetNextChar(); + if (c == '*') + { + while (m_pCur != m_pEnd) + { + char c = GetNextChar(); + if (c == '*' && *m_pCur == '/') + { + break; + } + } + } + else if (c == '/') + { + while (m_pCur != m_pEnd) + { + char c = GetNextChar(); + if (c == '\r' || c == '\n') + { + break; + } + } + } +} + +void JReader::readNumber() +{ + while (m_pCur != m_pEnd) + { + char c = *m_pCur; + if ((c >= '0' && c <= '9') || (c == '.' || c == 'e' || c == 'E' || c == '+' || c == '-')) + { + ++m_pCur; + } + else + { + break; + } + } +} + +bool JReader::readString() +{ + char c = 0; + while (m_pCur != m_pEnd) + { + c = GetNextChar(); + if ('\\' == c) + { + GetNextChar(); + } + else if ('"' == c) + { + break; + } + } + return ('"' == c); +} + +bool JReader::readObject(JValue &jval) +{ + string name; + Token tokenName; + jval = JValue(JValue::E_OBJECT); + while (readToken(tokenName)) + { + if (Token::E_ObjectEnd == tokenName.type) + { //empty + return true; + } + + if (Token::E_String != tokenName.type) + { + break; + } + + if (!decodeString(tokenName, name)) + { + return false; + } + + Token colon; + readToken(colon); + if (Token::E_MemberSeparator != colon.type) + { + return addError("Missing ':' after object member name", colon.pbeg); + } + + if (!readValue(jval[name.c_str()])) + { // error already set + return false; + } + + Token comma; + readToken(comma); + if (Token::E_ObjectEnd == comma.type) + { + return true; + } + + if (Token::E_ArraySeparator != comma.type) + { + return addError("Missing ',' or '}' in object declaration", comma.pbeg); + } + } + return addError("Missing '}' or object member name", tokenName.pbeg); +} + +bool JReader::readArray(JValue &jval) +{ + jval = JValue(JValue::E_ARRAY); + skipSpaces(); + if (']' == *m_pCur) // empty array + { + Token endArray; + readToken(endArray); + return true; + } + + size_t index = 0; + while (true) + { + if (!readValue(jval[index++])) + { //error already set + return false; + } + + Token token; + readToken(token); + if (Token::E_ArrayEnd == token.type) + { + break; + } + if (Token::E_ArraySeparator != token.type) + { + return addError("Missing ',' or ']' in array declaration", token.pbeg); + } + } + return true; +} + +bool JReader::decodeNumber(Token &token, JValue &jval) +{ + int64_t val = 0; + bool isNeg = false; + const char *pcur = token.pbeg; + if ('-' == *pcur) + { + pcur++; + isNeg = true; + } + for (const char *p = pcur; p != token.pend; p++) + { + char c = *p; + if ('.' == c || 'e' == c || 'E' == c) + { + return decodeDouble(token, jval); + } + else if (c < '0' || c > '9') + { + return addError("'" + string(token.pbeg, token.pend) + "' is not a number.", token.pbeg); + } + else + { + val = val * 10 + (c - '0'); + } + } + jval = isNeg ? -val : val; + return true; +} + +bool JReader::decodeDouble(Token &token, JValue &jval) +{ + const size_t szbuf = 512; + size_t len = size_t(token.pend - token.pbeg); + if (len <= szbuf) + { + char buf[szbuf]; + memcpy(buf, token.pbeg, len); + buf[len] = 0; + double val = 0; + if (1 == sscanf(buf, "%lf", &val)) + { + jval = val; + return true; + } + } + return addError("'" + string(token.pbeg, token.pend) + "' is too large or not a number.", token.pbeg); +} + +bool JReader::decodeString(Token &token, string &strdec) +{ + strdec = ""; + const char *pcur = token.pbeg + 1; + const char *pend = token.pend - 1; + strdec.reserve(size_t(token.pend - token.pbeg)); + while (pcur != pend) + { + char c = *pcur++; + if ('\\' == c) + { + if (pcur != pend) + { + char escape = *pcur++; + switch (escape) + { + case '"': + strdec += '"'; + break; + case '\\': + strdec += '\\'; + break; + case 'b': + strdec += '\b'; + break; + case 'f': + strdec += '\f'; + break; + case 'n': + strdec += '\n'; + break; + case 'r': + strdec += '\r'; + break; + case 't': + strdec += '\t'; + break; + case '/': + strdec += '/'; + break; + case 'u': + { // based on description from http://en.wikipedia.org/wiki/UTF-8 + + string strUnic; + strUnic.append(pcur, 4); + + pcur += 4; + + unsigned int cp = 0; + if (1 != sscanf(strUnic.c_str(), "%x", &cp)) + { + return addError("Bad escape sequence in string", pcur); + } + + string strUTF8; + + if (cp <= 0x7f) + { + strUTF8.resize(1); + strUTF8[0] = static_cast(cp); + } + else if (cp <= 0x7FF) + { + strUTF8.resize(2); + strUTF8[1] = static_cast(0x80 | (0x3f & cp)); + strUTF8[0] = static_cast(0xC0 | (0x1f & (cp >> 6))); + } + else if (cp <= 0xFFFF) + { + strUTF8.resize(3); + strUTF8[2] = static_cast(0x80 | (0x3f & cp)); + strUTF8[1] = 0x80 | static_cast((0x3f & (cp >> 6))); + strUTF8[0] = 0xE0 | static_cast((0xf & (cp >> 12))); + } + else if (cp <= 0x10FFFF) + { + strUTF8.resize(4); + strUTF8[3] = static_cast(0x80 | (0x3f & cp)); + strUTF8[2] = static_cast(0x80 | (0x3f & (cp >> 6))); + strUTF8[1] = static_cast(0x80 | (0x3f & (cp >> 12))); + strUTF8[0] = static_cast(0xF0 | (0x7 & (cp >> 18))); + } + + strdec += strUTF8; + } + break; + default: + return addError("Bad escape sequence in string", pcur); + break; + } + } + else + { + return addError("Empty escape sequence in string", pcur); + } + } + else if ('"' == c) + { + break; + } + else + { + strdec += c; + } + } + return true; +} + +bool JReader::addError(const string &message, const char *ploc) +{ + m_pErr = ploc; + m_strErr = message; + return false; +} + +char JReader::GetNextChar() +{ + return (m_pCur == m_pEnd) ? 0 : *m_pCur++; +} + +void JReader::error(string &strmsg) const +{ + strmsg = ""; + int row = 1; + const char *pcur = m_pBeg; + const char *plast = m_pBeg; + while (pcur < m_pErr && pcur <= m_pEnd) + { + char c = *pcur++; + if (c == '\r' || c == '\n') + { + if (c == '\r' && *pcur == '\n') + { + pcur++; + } + row++; + plast = pcur; + } + } + char msg[64]; + sprintf(msg, "Error: Line %d, Column %d, ", row, int(m_pErr - plast) + 1); + strmsg += msg + m_strErr + "\n"; +} + +// Class Writer +// ////////////////////////////////////////////////////////////////// +void JWriter::FastWrite(const JValue &jval, string &strDoc) +{ + strDoc = ""; + FastWriteValue(jval, strDoc); + //strDoc += "\n"; +} + +void JWriter::FastWriteValue(const JValue &jval, string &strDoc) +{ + switch (jval.type()) + { + case JValue::E_NULL: + strDoc += "null"; + break; + case JValue::E_INT: + strDoc += v2s(jval.asInt64()); + break; + case JValue::E_BOOL: + strDoc += jval.asBool() ? "true" : "false"; + break; + case JValue::E_FLOAT: + strDoc += v2s(jval.asFloat()); + break; + case JValue::E_STRING: + strDoc += v2s(jval.asCString()); + break; + case JValue::E_ARRAY: + { + strDoc += "["; + size_t usize = jval.size(); + for (size_t i = 0; i < usize; i++) + { + strDoc += (i > 0) ? "," : ""; + FastWriteValue(jval[i], strDoc); + } + strDoc += "]"; + } + break; + case JValue::E_OBJECT: + { + strDoc += "{"; + vector arrKeys; + jval.keys(arrKeys); + size_t usize = arrKeys.size(); + for (size_t i = 0; i < usize; i++) + { + const string &name = arrKeys[i]; + strDoc += (i > 0) ? "," : ""; + strDoc += v2s(name.c_str()) + ":"; + FastWriteValue(jval[name.c_str()], strDoc); + } + strDoc += "}"; + } + break; + case JValue::E_DATE: + { + strDoc += "\"date:"; + strDoc += d2s(jval.asDate()); + strDoc += "\""; + } + break; + case JValue::E_DATA: + { + strDoc += "\"data:"; + const string &strData = jval.asData(); + ZBase64 b64; + strDoc += b64.Encode(strData.data(), (int)strData.size()); + strDoc += "\""; + } + break; + } +} + +const string &JWriter::StyleWrite(const JValue &jval) +{ + m_strDoc = ""; + m_strTab = ""; + m_bAddChild = false; + StyleWriteValue(jval); + m_strDoc += "\n"; + return m_strDoc; +} + +void JWriter::StyleWriteValue(const JValue &jval) +{ + switch (jval.type()) + { + case JValue::E_NULL: + PushValue("null"); + break; + case JValue::E_INT: + PushValue(v2s(jval.asInt64())); + break; + case JValue::E_BOOL: + PushValue(jval.asBool() ? "true" : "false"); + break; + case JValue::E_FLOAT: + PushValue(v2s(jval.asFloat())); + break; + case JValue::E_STRING: + PushValue(v2s(jval.asCString())); + break; + case JValue::E_ARRAY: + StyleWriteArrayValue(jval); + break; + case JValue::E_OBJECT: + { + vector arrKeys; + jval.keys(arrKeys); + if (!arrKeys.empty()) + { + m_strDoc += '\n' + m_strTab + "{"; + m_strTab += '\t'; + size_t usize = arrKeys.size(); + for (size_t i = 0; i < usize; i++) + { + const string &name = arrKeys[i]; + m_strDoc += (i > 0) ? "," : ""; + m_strDoc += '\n' + m_strTab + v2s(name.c_str()) + " : "; + StyleWriteValue(jval[name]); + } + m_strTab.resize(m_strTab.size() - 1); + m_strDoc += '\n' + m_strTab + "}"; + } + else + { + PushValue("{}"); + } + } + break; + case JValue::E_DATE: + { + string strDoc; + strDoc += "\"date:"; + strDoc += d2s(jval.asDate()); + strDoc += "\""; + PushValue(strDoc); + } + break; + case JValue::E_DATA: + { + string strDoc; + strDoc += "\"data:"; + const string &strData = jval.asData(); + ZBase64 b64; + strDoc += b64.Encode(strData.data(), (int)strData.size()); + strDoc += "\""; + PushValue(strDoc); + } + break; + } +} + +void JWriter::StyleWriteArrayValue(const JValue &jval) +{ + size_t usize = jval.size(); + if (usize > 0) + { + bool isArrayMultiLine = isMultineArray(jval); + if (isArrayMultiLine) + { + m_strDoc += '\n' + m_strTab + "["; + m_strTab += '\t'; + bool hasChildValue = !m_childValues.empty(); + for (size_t i = 0; i < usize; i++) + { + m_strDoc += (i > 0) ? "," : ""; + if (hasChildValue) + { + m_strDoc += '\n' + m_strTab + m_childValues[i]; + } + else + { + m_strDoc += '\n' + m_strTab; + StyleWriteValue(jval[i]); + } + } + m_strTab.resize(m_strTab.size() - 1); + m_strDoc += '\n' + m_strTab + "]"; + } + else + { + m_strDoc += "[ "; + for (size_t i = 0; i < usize; ++i) + { + m_strDoc += (i > 0) ? ", " : ""; + m_strDoc += m_childValues[i]; + } + m_strDoc += " ]"; + } + } + else + { + PushValue("[]"); + } +} + +bool JWriter::isMultineArray(const JValue &jval) +{ + m_childValues.clear(); + size_t usize = jval.size(); + bool isMultiLine = (usize >= 25); + if (!isMultiLine) + { + for (size_t i = 0; i < usize; i++) + { + if (jval[i].size() > 0) + { + isMultiLine = true; + break; + } + } + } + if (!isMultiLine) + { + m_bAddChild = true; + m_childValues.reserve(usize); + size_t lineLength = 4 + (usize - 1) * 2; // '[ ' + ', '*n + ' ]' + for (size_t i = 0; i < usize; i++) + { + StyleWriteValue(jval[i]); + lineLength += m_childValues[i].length(); + } + m_bAddChild = false; + isMultiLine = lineLength >= 75; + } + return isMultiLine; +} + +void JWriter::PushValue(const string &strval) +{ + if (!m_bAddChild) + { + m_strDoc += strval; + } + else + { + m_childValues.push_back(strval); + } +} + +string JWriter::v2s(int64_t val) +{ + char buf[32]; + sprintf(buf, "%" PRId64, val); + return buf; +} + +string JWriter::v2s(double val) +{ + char buf[512]; + sprintf(buf, "%g", val); + return buf; +} + +string JWriter::d2s(time_t t) +{ + //t = (t > 0x7933F8EFF) ? (0x7933F8EFF - 1) : t; + + tm ft = {0}; + +#ifdef _WIN32 + localtime_s(&ft, &t); +#else + localtime_r(&t, &ft); +#endif + + ft.tm_year = (ft.tm_year < 0) ? 0 : ft.tm_year; + ft.tm_mon = (ft.tm_mon < 0) ? 0 : ft.tm_mon; + ft.tm_mday = (ft.tm_mday < 0) ? 0 : ft.tm_mday; + ft.tm_hour = (ft.tm_hour < 0) ? 0 : ft.tm_hour; + ft.tm_min = (ft.tm_min < 0) ? 0 : ft.tm_min; + ft.tm_sec = (ft.tm_sec < 0) ? 0 : ft.tm_sec; + + char szDate[64] = {0}; + sprintf(szDate, "%04d-%02d-%02dT%02d:%02d:%02dZ", ft.tm_year + 1900, ft.tm_mon + 1, ft.tm_mday, ft.tm_hour, ft.tm_min, ft.tm_sec); + return szDate; +} + +string JWriter::v2s(const char *pstr) +{ + if (NULL != strpbrk(pstr, "\"\\\b\f\n\r\t")) + { + string ret; + ret.reserve(strlen(pstr) * 2 + 3); + ret += "\""; + for (const char *c = pstr; 0 != *c; c++) + { + switch (*c) + { + case '\\': + { + c++; + bool bUnicode = false; + if ('u' == *c) + { + bool bFlag = true; + for (int i = 1; i <= 4; i++) + { + if (!isdigit(*(c + i))) + { + bFlag = false; + break; + } + } + bUnicode = bFlag; + } + + if (true == bUnicode) + { + ret += "\\u"; + } + else + { + ret += "\\\\"; + c--; + } + } + break; + case '\"': + ret += "\\\""; + break; + case '\b': + ret += "\\b"; + break; + case '\f': + ret += "\\f"; + break; + case '\n': + ret += "\\n"; + break; + case '\r': + ret += "\\r"; + break; + case '\t': + ret += "\\t"; + break; + default: + ret += *c; + break; + } + } + ret += "\""; + return ret; + } + else + { + return string("\"") + pstr + "\""; + } +} + +std::string JWriter::vstring2s(const char *pstr) +{ + return string("\\\"") + pstr + "\\\""; +} + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +#define BE16TOH(x) ((((x)&0xFF00) >> 8) | (((x)&0x00FF) << 8)) + +#define BE32TOH(x) ((((x)&0xFF000000) >> 24) | (((x)&0x00FF0000) >> 8) | (((x)&0x0000FF00) << 8) | (((x)&0x000000FF) << 24)) + +#define BE64TOH(x) ((((x)&0xFF00000000000000ull) >> 56) | (((x)&0x00FF000000000000ull) >> 40) | (((x)&0x0000FF0000000000ull) >> 24) | (((x)&0x000000FF00000000ull) >> 8) | (((x)&0x00000000FF000000ull) << 8) | (((x)&0x0000000000FF0000ull) << 24) | (((x)&0x000000000000FF00ull) << 40) | (((x)&0x00000000000000FFull) << 56)) + +////////////////////////////////////////////////////////////////////////// +PReader::PReader() +{ + //xml + m_pBeg = NULL; + m_pEnd = NULL; + m_pCur = NULL; + m_pErr = NULL; + + //binary + m_pTrailer = NULL; + m_uObjects = 0; + m_uOffsetSize = 0; + m_pOffsetTable = 0; + m_uDictParamSize = 0; +} + +bool PReader::parse(const char *pdoc, size_t len, JValue &root) +{ + root.clear(); + if (NULL == pdoc) + { + return false; + } + + if (len < 30) + { + return false; + } + + if (0 == memcmp(pdoc, "bplist00", 8)) + { + return parseBinary(pdoc, len, root); + } + else + { + m_pBeg = pdoc; + m_pEnd = m_pBeg + len; + m_pCur = m_pBeg; + m_pErr = m_pBeg; + m_strErr = "null"; + + Token token; + readToken(token); + return readValue(root, token); + } +} + +bool PReader::readValue(JValue &pval, Token &token) +{ + switch (token.type) + { + case Token::E_True: + pval = true; + break; + case Token::E_False: + pval = false; + break; + case Token::E_Null: + pval = JValue(); + break; + case Token::E_Integer: + return decodeNumber(token, pval); + break; + case Token::E_Real: + return decodeDouble(token, pval); + break; + case Token::E_ArrayNull: + pval = JValue(JValue::E_ARRAY); + break; + case Token::E_ArrayBegin: + return readArray(pval); + break; + case Token::E_DictionaryNull: + pval = JValue(JValue::E_OBJECT); + break; + case Token::E_DictionaryBegin: + return readDictionary(pval); + break; + case Token::E_Date: + { + string strval; + decodeString(token, strval); + + tm ft = {0}; + sscanf(strval.c_str(), "%04d-%02d-%02dT%02d:%02d:%02dZ", &ft.tm_year, &ft.tm_mon, &ft.tm_mday, &ft.tm_hour, &ft.tm_min, &ft.tm_sec); + ft.tm_mon -= 1; + ft.tm_year -= 1900; + pval.assignDate(mktime(&ft)); + } + break; + case Token::E_Data: + { + string strval; + decodeString(token, strval); + + ZBase64 b64; + int nDecLen = 0; + const char *data = b64.Decode(strval.data(), (int)strval.size(), &nDecLen); + pval.assignData(data, nDecLen); + } + break; + case Token::E_String: + { + string strval; + decodeString(token, strval, false); + XMLUnescape(strval); + pval = strval.c_str(); + } + break; + default: + return addError("Syntax error: value, dictionary or array expected.", token.pbeg); + break; + } + return true; +} + +bool PReader::readLabel(string &label) +{ + skipSpaces(); + + char c = *m_pCur++; + if ('<' != c) + { + return false; + } + + label.clear(); + label.reserve(10); + label += c; + + bool bEnd = false; + while (m_pCur != m_pEnd) + { + c = *m_pCur++; + if ('>' == c) + { + if ('/' == *(m_pCur - 1) || '?' == *(m_pCur - 1)) + { + label += *(m_pCur - 1); + } + + label += c; + break; + } + else if (' ' == c) + { + bEnd = true; + } + else if (!bEnd) + { + label += c; + } + } + + if ('>' != c) + { + label.clear(); + return false; + } + + return (!label.empty()); +} + +void PReader::endLabel(Token &token, const char *szLabel) +{ + string label; + readLabel(label); + if (szLabel != label) + { + token.type = Token::E_Error; + } +} + +bool PReader::readToken(Token &token) +{ + string label; + if (!readLabel(label)) + { + token.type = Token::E_Error; + return false; + } + + if ('?' == label.at(1) || '!' == label.at(1)) + { + return readToken(token); + } + + if ("" == label) + { + token.type = Token::E_DictionaryBegin; + } + else if ("" == label) + { + token.type = Token::E_DictionaryEnd; + } + else if ("" == label) + { + token.type = Token::E_ArrayBegin; + } + else if ("" == label) + { + token.type = Token::E_ArrayEnd; + } + else if ("" == label) + { + token.pbeg = m_pCur; + token.type = readString() ? Token::E_Key : Token::E_Error; + token.pend = m_pCur; + + endLabel(token, ""); + } + else if ("" == label) + { + token.type = Token::E_Key; + } + else if ("" == label) + { + token.pbeg = m_pCur; + token.type = readString() ? Token::E_String : Token::E_Error; + token.pend = m_pCur; + + endLabel(token, ""); + } + else if ("" == label) + { + token.pbeg = m_pCur; + token.type = readString() ? Token::E_Date : Token::E_Error; + token.pend = m_pCur; + + endLabel(token, ""); + } + else if ("" == label) + { + token.pbeg = m_pCur; + token.type = readString() ? Token::E_Data : Token::E_Error; + token.pend = m_pCur; + + endLabel(token, ""); + } + else if ("" == label) + { + token.pbeg = m_pCur; + token.type = readNumber() ? Token::E_Integer : Token::E_Error; + token.pend = m_pCur; + + endLabel(token, ""); + } + else if ("" == label) + { + token.pbeg = m_pCur; + token.type = readNumber() ? Token::E_Real : Token::E_Error; + token.pend = m_pCur; + + endLabel(token, ""); + } + else if ("" == label) + { + token.type = Token::E_True; + } + else if ("" == label) + { + token.type = Token::E_False; + } + else if ("" == label) + { + token.type = Token::E_ArrayNull; + } + else if ("" == label) + { + token.type = Token::E_DictionaryNull; + } + else if ("" == label || "" == label || "" == label || "" == label || "" == label) + { + token.type = Token::E_Null; + } + else if ("" == label) + { + return readToken(token); + } + else if ("" == label || "" == label) + { + token.type = Token::E_End; + } + else + { + token.type = Token::E_Error; + } + + return true; +} + +void PReader::skipSpaces() +{ + while (m_pCur != m_pEnd) + { + char c = *m_pCur; + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + { + m_pCur++; + } + else + { + break; + } + } +} + +bool PReader::readNumber() +{ + while (m_pCur != m_pEnd) + { + char c = *m_pCur; + if ((c >= '0' && c <= '9') || (c == '.' || c == 'e' || c == 'E' || c == '+' || c == '-')) + { + ++m_pCur; + } + else + { + break; + } + } + return true; +} + +bool PReader::readString() +{ + while (m_pCur != m_pEnd) + { + if ('<' == *m_pCur) + { + break; + } + m_pCur++; + } + return ('<' == *m_pCur); +} + +bool PReader::readDictionary(JValue &pval) +{ + Token key; + string strKey; + pval = JValue(JValue::E_OBJECT); + while (readToken(key)) + { + if (Token::E_DictionaryEnd == key.type) + { //empty + return true; + } + + if (Token::E_Key != key.type) + { + break; + } + + strKey = ""; + if (!decodeString(key, strKey)) + { + return false; + } + XMLUnescape(strKey); + + Token val; + readToken(val); + if (!readValue(pval[strKey.c_str()], val)) + { + return false; + } + } + return addError("Missing '' or dictionary member name", key.pbeg); +} + +bool PReader::readArray(JValue &pval) +{ + pval = JValue(JValue::E_ARRAY); + + size_t index = 0; + while (true) + { + Token token; + readToken(token); + if (Token::E_ArrayEnd == token.type) + { + return true; + } + + if (!readValue(pval[index++], token)) + { + return false; + } + } + + return true; +} + +bool PReader::decodeNumber(Token &token, JValue &pval) +{ + int64_t val = 0; + bool isNeg = false; + const char *pcur = token.pbeg; + if ('-' == *pcur) + { + pcur++; + isNeg = true; + } + for (const char *p = pcur; p != token.pend; p++) + { + char c = *p; + if ('.' == c || 'e' == c || 'E' == c) + { + return decodeDouble(token, pval); + } + else if (c < '0' || c > '9') + { + return addError("'" + string(token.pbeg, token.pend) + "' is not a number.", token.pbeg); + } + else + { + val = val * 10 + (c - '0'); + } + } + pval = isNeg ? -val : val; + return true; +} + +bool PReader::decodeDouble(Token &token, JValue &pval) +{ + const size_t szbuf = 512; + size_t len = size_t(token.pend - token.pbeg); + if (len <= szbuf) + { + char buf[szbuf]; + memcpy(buf, token.pbeg, len); + buf[len] = 0; + double val = 0; + if (1 == sscanf(buf, "%lf", &val)) + { + pval = val; + return true; + } + } + return addError("'" + string(token.pbeg, token.pend) + "' is too large or not a number.", token.pbeg); +} + +bool PReader::decodeString(Token &token, string &strdec, bool filter) +{ + const char *pcur = token.pbeg; + const char *pend = token.pend; + strdec.reserve(size_t(token.pend - token.pbeg) + 6); + while (pcur != pend) + { + char c = *pcur++; + if (filter && ('\n' == c || '\r' == c || '\t' == c)) + { + continue; + } + strdec += c; + } + return true; +} + +bool PReader::addError(const string &message, const char *ploc) +{ + m_pErr = ploc; + m_strErr = message; + return false; +} + +void PReader::error(string &strmsg) const +{ + strmsg = ""; + int row = 1; + const char *pcur = m_pBeg; + const char *plast = m_pBeg; + while (pcur < m_pErr && pcur <= m_pEnd) + { + char c = *pcur++; + if (c == '\r' || c == '\n') + { + if (c == '\r' && *pcur == '\n') + { + pcur++; + } + row++; + plast = pcur; + } + } + char msg[64]; + sprintf(msg, "Error: Line %d, Column %d, ", row, int(m_pErr - plast) + 1); + strmsg += msg + m_strErr + "\n"; +} + +////////////////////////////////////////////////////////////////////////// +uint32_t PReader::getUInt24FromBE(const char *v) +{ + uint32_t ret = 0; + uint8_t *tmp = (uint8_t *)&ret; + memcpy(tmp, v, 3 * sizeof(char)); + byteConvert(tmp, sizeof(uint32_t)); + return ret; +} + +uint64_t PReader::getUIntVal(const char *v, size_t size) +{ + if (8 == size) + return BE64TOH(*((uint64_t *)v)); + else if (4 == size) + return BE32TOH(*((uint32_t *)v)); + else if (3 == size) + return getUInt24FromBE(v); + else if (2 == size) + return BE16TOH(*((uint16_t *)v)); + else + return *((uint8_t *)v); +} + +void PReader::byteConvert(uint8_t *v, size_t size) +{ + uint8_t tmp = 0; + for (size_t i = 0, j = 0; i < (size / 2); i++) + { + tmp = v[i]; + j = (size - 1) - i; + v[i] = v[j]; + v[j] = tmp; + } +} + +bool PReader::readUIntSize(const char *&pcur, size_t &size) +{ + JValue temp; + readBinaryValue(pcur, temp); + if (temp.isInt()) + { + size = (size_t)temp.asInt64(); + return true; + } + + assert(0); + return false; +} + +bool PReader::readUnicode(const char *pcur, size_t size, JValue &pv) +{ + if (0 == size) + { + pv = ""; + return false; + } + + uint16_t *unistr = (uint16_t *)malloc(2 * size); + memcpy(unistr, pcur, 2 * size); + for (size_t i = 0; i < size; i++) + { + byteConvert((uint8_t *)(unistr + i), 2); + } + + char *outbuf = (char *)malloc(3 * (size + 1)); + + size_t p = 0; + size_t i = 0; + uint16_t wc = 0; + while (i < size) + { + wc = unistr[i++]; + if (wc >= 0x800) + { + outbuf[p++] = (char)(0xE0 + ((wc >> 12) & 0xF)); + outbuf[p++] = (char)(0x80 + ((wc >> 6) & 0x3F)); + outbuf[p++] = (char)(0x80 + (wc & 0x3F)); + } + else if (wc >= 0x80) + { + outbuf[p++] = (char)(0xC0 + ((wc >> 6) & 0x1F)); + outbuf[p++] = (char)(0x80 + (wc & 0x3F)); + } + else + { + outbuf[p++] = (char)(wc & 0x7F); + } + } + + outbuf[p] = 0; + + pv = outbuf; + + free(outbuf); + outbuf = NULL; + free(unistr); + unistr = NULL; + + return true; +} + +bool PReader::readBinaryValue(const char *&pcur, JValue &pv) +{ + enum + { + BPLIST_NULL = 0x00, + BPLIST_FALSE = 0x08, + BPLIST_TRUE = 0x09, + BPLIST_FILL = 0x0F, + BPLIST_UINT = 0x10, + BPLIST_REAL = 0x20, + BPLIST_DATE = 0x30, + BPLIST_DATA = 0x40, + BPLIST_STRING = 0x50, + BPLIST_UNICODE = 0x60, + BPLIST_UNK_0x70 = 0x70, + BPLIST_UID = 0x80, + BPLIST_ARRAY = 0xA0, + BPLIST_SET = 0xC0, + BPLIST_DICT = 0xD0, + BPLIST_MASK = 0xF0 + }; + + uint8_t c = *pcur++; + uint8_t key = c & 0xF0; + uint8_t val = c & 0x0F; + + switch (key) + { + case BPLIST_NULL: + { + switch (val) + { + case BPLIST_TRUE: + { + pv = true; + } + break; + case BPLIST_FALSE: + { + pv = false; + } + break; + case BPLIST_NULL: + { + } + break; + default: + { + assert(0); + return false; + } + break; + } + } + break; + case BPLIST_UID: + case BPLIST_UINT: + { + size_t size = 1 << val; + switch (size) + { + case sizeof(uint8_t): + case sizeof(uint16_t): + case sizeof(uint32_t): + case sizeof(uint64_t): + { + pv = (int64_t)getUIntVal(pcur, size); + } + break; + default: + { + assert(0); + return false; + } + break; + }; + + pcur += size; + } + break; + case BPLIST_REAL: + { + size_t size = 1 << val; + + uint8_t *buf = (uint8_t *)malloc(size); + memcpy(buf, pcur, size); + byteConvert(buf, size); + + switch (size) + { + case sizeof(float): + pv = (double)(*(float *)buf); + case sizeof(double): + pv = (*(double *)buf); + break; + default: + { + assert(0); + free(buf); + return false; + } + break; + } + + free(buf); + } + break; + + case BPLIST_DATE: + { + if (3 == val) + { + size_t size = 1 << val; + uint8_t *buf = (uint8_t *)malloc(size); + memcpy(buf, pcur, size); + byteConvert(buf, size); + pv.assignDate(((time_t)(*(double *)buf)) + 978278400); + free(buf); + } + else + { + assert(0); + return false; + } + } + break; + + case BPLIST_DATA: + { + size_t size = val; + if (0x0F == val) + { + if (!readUIntSize(pcur, size)) + { + return false; + } + } + pv.assignData(pcur, size); + } + break; + + case BPLIST_STRING: + { + size_t size = val; + if (0x0F == val) + { + if (!readUIntSize(pcur, size)) + { + return false; + } + } + + string strval; + strval.append(pcur, size); + strval.append(1, 0); + pv = strval.c_str(); + } + break; + + case BPLIST_UNICODE: + { + size_t size = val; + if (0x0F == val) + { + if (!readUIntSize(pcur, size)) + { + return false; + } + } + + readUnicode(pcur, size, pv); + } + break; + case BPLIST_ARRAY: + case BPLIST_UNK_0x70: + { + size_t size = val; + if (0x0F == val) + { + if (!readUIntSize(pcur, size)) + { + return false; + } + } + + for (size_t i = 0; i < size; i++) + { + uint64_t uIndex = getUIntVal((const char *)pcur + i * m_uDictParamSize, m_uDictParamSize); + if (uIndex < m_uObjects) + { + const char *pval = (m_pBeg + getUIntVal(m_pOffsetTable + uIndex * m_uOffsetSize, m_uOffsetSize)); + readBinaryValue(pval, pv[i]); + } + else + { + assert(0); + return false; + } + } + } + break; + + case BPLIST_SET: + case BPLIST_DICT: + { + size_t size = val; + if (0x0F == val) + { + if (!readUIntSize(pcur, size)) + { + return false; + } + } + + for (size_t i = 0; i < size; i++) + { + JValue pvKey; + JValue pvVal; + + uint64_t uKeyIndex = getUIntVal((const char *)pcur + i * m_uDictParamSize, m_uDictParamSize); + uint64_t uValIndex = getUIntVal((const char *)pcur + (i + size) * m_uDictParamSize, m_uDictParamSize); + + if (uKeyIndex < m_uObjects) + { + const char *pval = (m_pBeg + getUIntVal(m_pOffsetTable + uKeyIndex * m_uOffsetSize, m_uOffsetSize)); + readBinaryValue(pval, pvKey); + } + + if (uValIndex < m_uObjects) + { + const char *pval = (m_pBeg + getUIntVal(m_pOffsetTable + uValIndex * m_uOffsetSize, m_uOffsetSize)); + readBinaryValue(pval, pvVal); + } + + if (pvKey.isString() && !pvVal.isNull()) + { + pv[pvKey.asCString()] = pvVal; + } + } + } + break; + default: + { + assert(0); + return false; + } + } + + return true; +} + +bool PReader::parseBinary(const char *pbdoc, size_t len, JValue &pv) +{ + m_pBeg = pbdoc; + + m_pTrailer = m_pBeg + len - 26; + + m_uOffsetSize = m_pTrailer[0]; + m_uDictParamSize = m_pTrailer[1]; + m_uObjects = getUIntVal(m_pTrailer + 2, 8); + + if (0 == m_uObjects) + { + return false; + } + + m_pOffsetTable = m_pBeg + getUIntVal(m_pTrailer + 18, 8); + const char *pval = (m_pBeg + getUIntVal(m_pOffsetTable, m_uOffsetSize)); + return readBinaryValue(pval, pv); +} + +void PReader::XMLUnescape(string &strval) +{ + PWriter::StringReplace(strval, "&", "&"); + PWriter::StringReplace(strval, "<", "<"); + //PWriter::StringReplace(strval,">", ">"); //optional + //PWriter::StringReplace(strval, "'", "'"); //optional + //PWriter::StringReplace(strval, """, "\""); //optional +} + +////////////////////////////////////////////////////////////////////////// +void PWriter::FastWrite(const JValue &pval, string &strdoc) +{ + strdoc.clear(); + strdoc = "\n" + "\n" + "\n"; + + string strindent; + FastWriteValue(pval, strdoc, strindent); + + strdoc += ""; +} + +void PWriter::FastWriteValue(const JValue &pval, string &strdoc, string &strindent) +{ + if (pval.isObject()) + { + strdoc += strindent; + if (pval.isEmpty()) + { + strdoc += "\n"; + return; + } + strdoc += "\n"; + vector arrKeys; + if (pval.keys(arrKeys)) + { + strindent.push_back('\t'); + for (size_t i = 0; i < arrKeys.size(); i++) + { + if (!pval[arrKeys[i].c_str()].isNull()) + { + string strkey = arrKeys[i]; + XMLEscape(strkey); + strdoc += strindent; + strdoc += ""; + strdoc += strkey; + strdoc += "\n"; + FastWriteValue(pval[arrKeys[i].c_str()], strdoc, strindent); + } + } + strindent.erase(strindent.end() - 1); + } + strdoc += strindent; + strdoc += "\n"; + } + else if (pval.isArray()) + { + strdoc += strindent; + if (pval.isEmpty()) + { + strdoc += "\n"; + return; + } + strdoc += "\n"; + strindent.push_back('\t'); + for (size_t i = 0; i < pval.size(); i++) + { + FastWriteValue(pval[i], strdoc, strindent); + } + strindent.erase(strindent.end() - 1); + strdoc += strindent; + strdoc += "\n"; + } + else if (pval.isDate()) + { + strdoc += strindent; + strdoc += ""; + strdoc += JWriter::d2s(pval.asDate()); + strdoc += "\n"; + } + else if (pval.isData()) + { + ZBase64 b64; + string strdata = pval.asData(); + strdoc += strindent; + strdoc += "\n"; + strdoc += strindent; + strdoc += b64.Encode(strdata.data(), (int)strdata.size()); + strdoc += "\n"; + strdoc += strindent; + strdoc += "\n"; + } + else if (pval.isString()) + { + strdoc += strindent; + if (pval.isDateString()) + { + strdoc += ""; + strdoc += pval.asString().c_str() + 5; + strdoc += "\n"; + } + else if (pval.isDataString()) + { + strdoc += "\n"; + strdoc += strindent; + strdoc += pval.asString().c_str() + 5; + strdoc += "\n"; + strdoc += strindent; + strdoc += "\n"; + } + else + { + string strval = pval.asCString(); + XMLEscape(strval); + strdoc += ""; + strdoc += strval; + strdoc += "\n"; + } + } + else if (pval.isBool()) + { + strdoc += strindent; + strdoc += (pval.asBool() ? "\n" : "\n"); + } + else if (pval.isInt()) + { + strdoc += strindent; + strdoc += ""; + char temp[32] = {0}; + sprintf(temp, "%" PRId64, pval.asInt64()); + strdoc += temp; + strdoc += "\n"; + } + else if (pval.isFloat()) + { + strdoc += strindent; + strdoc += ""; + + double v = pval.asFloat(); + if (numeric_limits::infinity() == v) + { + strdoc += "+infinity"; + } + else + { + char temp[32] = {0}; + if (floor(v) == v) + { + sprintf(temp, "%" PRId64, (int64_t)v); + } + else + { + sprintf(temp, "%.15lf", v); + } + strdoc += temp; + } + + strdoc += "\n"; + } +} + +void PWriter::XMLEscape(string &strval) +{ + StringReplace(strval, "&", "&"); + StringReplace(strval, "<", "<"); + //StringReplace(strval, ">", ">"); //option + //StringReplace(strval, "'", "'"); //option + //StringReplace(strval, "\"", """); //option +} + +string &PWriter::StringReplace(string &context, const string &from, const string &to) +{ + size_t lookHere = 0; + size_t foundHere; + while ((foundHere = context.find(from, lookHere)) != string::npos) + { + context.replace(foundHere, from.size(), to); + lookHere = foundHere + to.size(); + } + return context; +} diff --git a/ZSign/common/json.h b/ZSign/common/json.h new file mode 100644 index 0000000..9ef6dd1 --- /dev/null +++ b/ZSign/common/json.h @@ -0,0 +1,414 @@ +#ifndef JSON_INCLUDED +#define JSON_INCLUDED + +#ifdef _WIN32 + +typedef signed char int8_t; +typedef short int int16_t; +typedef int int32_t; +typedef long long int int64_t; +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long int uint64_t; + +#else + +#include +#include +#include + +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std; + + + +class JValue +{ +public: + enum TYPE + { + E_NULL = 0, + E_INT, + E_BOOL, + E_FLOAT, + E_ARRAY, + E_OBJECT, + E_STRING, + E_DATE, + E_DATA, + }; + +public: + JValue(TYPE type = E_NULL); + JValue(int val); + JValue(bool val); + JValue(double val); + JValue(int64_t val); + JValue(const char *val); + JValue(const string &val); + JValue(const JValue &other); + JValue(const char *val, size_t len); + ~JValue(); + +public: + int asInt() const; + bool asBool() const; + double asFloat() const; + int64_t asInt64() const; + string asString() const; + const char *asCString() const; + time_t asDate() const; + string asData() const; + + void assignData(const char *val, size_t size); + void assignDate(time_t val); + void assignDateString(time_t val); + + TYPE type() const; + size_t size() const; + void clear(); + + JValue &at(int index); + JValue &at(size_t index); + JValue &at(const char *key); + + bool has(const char *key) const; + int index(const char *ele) const; + bool keys(vector &arrKeys) const; + + bool join(JValue &jv); + bool append(JValue &jv); + + bool remove(int index); + bool remove(size_t index); + bool remove(const char *key); + + JValue &back(); + JValue &front(); + + bool push_back(int val); + bool push_back(bool val); + bool push_back(double val); + bool push_back(int64_t val); + bool push_back(const char *val); + bool push_back(const string &val); + bool push_back(const JValue &jval); + bool push_back(const char *val, size_t len); + + bool isInt() const; + bool isNull() const; + bool isBool() const; + bool isFloat() const; + bool isArray() const; + bool isObject() const; + bool isString() const; + bool isEmpty() const; + bool isData() const; + bool isDate() const; + bool isDataString() const; + bool isDateString() const; + + operator int() const; + operator bool() const; + operator double() const; + operator int64_t() const; + operator string() const; + operator const char *() const; + + JValue &operator=(const JValue &other); + + JValue &operator[](int index); + const JValue &operator[](int index) const; + + JValue &operator[](size_t index); + const JValue &operator[](size_t index) const; + + JValue &operator[](int64_t index); + const JValue &operator[](int64_t index) const; + + JValue &operator[](const char *key); + const JValue &operator[](const char *key) const; + + JValue &operator[](const string &key); + const JValue &operator[](const string &key) const; + + friend bool operator==(const JValue &jv, const char *psz) + { + return (0 == strcmp(jv.asCString(), psz)); + } + + friend bool operator==(const char *psz, const JValue &jv) + { + return (0 == strcmp(jv.asCString(), psz)); + } + + friend bool operator!=(const JValue &jv, const char *psz) + { + return (0 != strcmp(jv.asCString(), psz)); + } + + friend bool operator!=(const char *psz, const JValue &jv) + { + return (0 != strcmp(jv.asCString(), psz)); + } + +private: + void Free(); + char *NewString(const char *cstr); + void CopyValue(const JValue &src); + bool WriteDataToFile(const char *file, const char *data, size_t len); + +public: + static const JValue null; + static const string nullData; + +private: + union HOLD { + bool vBool; + double vFloat; + int64_t vInt64; + char *vString; + vector *vArray; + map *vObject; + time_t vDate; + string *vData; + wchar_t *vUnicode; + } m_Value; + + TYPE m_eType; + +public: + string write() const; + const char *write(string &strDoc) const; + + string styleWrite() const; + const char *styleWrite(string &strDoc) const; + + bool read(const char *pdoc, string *pstrerr = NULL); + bool read(const string &strdoc, string *pstrerr = NULL); + + string writePList() const; + const char *writePList(string &strDoc) const; + + bool readPList(const string &strdoc, string *pstrerr = NULL); + bool readPList(const char *pdoc, size_t len = 0, string *pstrerr = NULL); + + bool readFile(const char *file, string *pstrerr = NULL); + bool readPListFile(const char *file, string *pstrerr = NULL); + + bool writeFile(const char *file); + bool writePListFile(const char *file); + bool styleWriteFile(const char *file); + + bool readPath(const char *path, ...); + bool readPListPath(const char *path, ...); + bool writePath(const char *path, ...); + bool writePListPath(const char *path, ...); + bool styleWritePath(const char *path, ...); +}; + +class JReader +{ +public: + bool parse(const char *pdoc, JValue &root); + void error(string &strmsg) const; + +private: + struct Token + { + enum TYPE + { + E_Error = 0, + E_End, + E_Null, + E_True, + E_False, + E_Number, + E_String, + E_ArrayBegin, + E_ArrayEnd, + E_ObjectBegin, + E_ObjectEnd, + E_ArraySeparator, + E_MemberSeparator + }; + TYPE type; + const char *pbeg; + const char *pend; + }; + + void skipSpaces(); + void skipComment(); + + bool match(const char *pattern, int patternLength); + + bool readToken(Token &token); + bool readValue(JValue &jval); + bool readArray(JValue &jval); + void readNumber(); + + bool readString(); + bool readObject(JValue &jval); + + bool decodeNumber(Token &token, JValue &jval); + bool decodeString(Token &token, string &decoded); + bool decodeDouble(Token &token, JValue &jval); + + char GetNextChar(); + bool addError(const string &message, const char *ploc); + +private: + const char *m_pBeg; + const char *m_pEnd; + const char *m_pCur; + const char *m_pErr; + string m_strErr; +}; + +class JWriter +{ +public: + static void FastWrite(const JValue &jval, string &strDoc); + static void FastWriteValue(const JValue &jval, string &strDoc); + +public: + const string &StyleWrite(const JValue &jval); + +private: + void PushValue(const string &strval); + void StyleWriteValue(const JValue &jval); + void StyleWriteArrayValue(const JValue &jval); + bool isMultineArray(const JValue &jval); + +public: + static string v2s(double val); + static string v2s(int64_t val); + static string v2s(const char *val); + + static string vstring2s(const char *val); + static string d2s(time_t t); + +private: + string m_strDoc; + string m_strTab; + bool m_bAddChild; + vector m_childValues; +}; + +////////////////////////////////////////////////////////////////////////// +class PReader +{ +public: + PReader(); + +public: + bool parse(const char *pdoc, size_t len, JValue &root); + void error(string &strmsg) const; + +private: + struct Token + { + enum TYPE + { + E_Error = 0, + E_End, + E_Null, + E_True, + E_False, + E_Key, + E_Data, + E_Date, + E_Integer, + E_Real, + E_String, + E_ArrayBegin, + E_ArrayEnd, + E_ArrayNull, + E_DictionaryBegin, + E_DictionaryEnd, + E_DictionaryNull, + E_ArraySeparator, + E_MemberSeparator + }; + + Token() + { + pbeg = NULL; + pend = NULL; + type = E_Error; + } + + TYPE type; + const char *pbeg; + const char *pend; + }; + + bool readToken(Token &token); + bool readLabel(string &label); + bool readValue(JValue &jval, Token &token); + bool readArray(JValue &jval); + bool readNumber(); + + bool readString(); + bool readDictionary(JValue &jval); + + void endLabel(Token &token, const char *szLabel); + + bool decodeNumber(Token &token, JValue &jval); + bool decodeString(Token &token, string &decoded, bool filter = true); + bool decodeDouble(Token &token, JValue &jval); + + void skipSpaces(); + bool addError(const string &message, const char *ploc); + +public: + bool parseBinary(const char *pbdoc, size_t len, JValue &pv); + +private: + uint32_t getUInt24FromBE(const char *v); + void byteConvert(uint8_t *v, size_t size); + uint64_t getUIntVal(const char *v, size_t size); + bool readUIntSize(const char *&pcur, size_t &size); + bool readBinaryValue(const char *&pcur, JValue &pv); + bool readUnicode(const char *pcur, size_t size, JValue &pv); + +public: + static void XMLUnescape(string &strval); + +private: //xml + const char *m_pBeg; + const char *m_pEnd; + const char *m_pCur; + const char *m_pErr; + string m_strErr; + +private: //binary + const char *m_pTrailer; + uint64_t m_uObjects; + uint8_t m_uOffsetSize; + const char *m_pOffsetTable; + uint8_t m_uDictParamSize; +}; + +class PWriter +{ +public: + static void FastWrite(const JValue &pval, string &strdoc); + static void FastWriteValue(const JValue &pval, string &strdoc, string &strindent); + +public: + static void XMLEscape(string &strval); + static string &StringReplace(string &context, const string &from, const string &to); +}; + +#endif // JSON_INCLUDED diff --git a/ZSign/common/mach-o.h b/ZSign/common/mach-o.h new file mode 100644 index 0000000..7c5deb9 --- /dev/null +++ b/ZSign/common/mach-o.h @@ -0,0 +1,580 @@ +#pragma once +#include +// typedef int cpu_type_t; +// typedef int cpu_subtype_t; +// typedef int vm_prot_t; + +// /* +// * Capability bits used in the definition of cpu_type. +// */ +// #define CPU_ARCH_MASK 0xff000000 /* mask for architecture bits */ +// #define CPU_ARCH_ABI64 0x01000000 /* 64 bit ABI */ +// #define CPU_ARCH_ABI64_32 0x02000000 + +// /* +// * Machine types known by all. +// */ +// #define CPU_TYPE_ANY -1 +// #define CPU_TYPE_VAX 1 +// #define CPU_TYPE_MC680x0 6 +// #define CPU_TYPE_X86 7 +// #define CPU_TYPE_I386 CPU_TYPE_X86 /* compatibility */ +// #define CPU_TYPE_MIPS 8 +// #define CPU_TYPE_MC98000 10 +// #define CPU_TYPE_HPPA 11 +// #define CPU_TYPE_ARM 12 +// #define CPU_TYPE_MC88000 13 +// #define CPU_TYPE_SPARC 14 +// #define CPU_TYPE_I860 15 +// #define CPU_TYPE_ALPHA 16 +// #define CPU_TYPE_POWERPC 18 +// #define CPU_TYPE_X86_64 (CPU_TYPE_X86 | CPU_ARCH_ABI64) +// #define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64) +// #define CPU_TYPE_ARM64_32 (CPU_TYPE_ARM | CPU_ARCH_ABI64_32) +// #define CPU_TYPE_POWERPC64 (CPU_TYPE_POWERPC | CPU_ARCH_ABI64) + +// /* +// * Machine subtypes (these are defined here, instead of in a machine +// * dependent directory, so that any program can get all definitions +// * regardless of where is it compiled). +// */ + +// /* +// * Capability bits used in the definition of cpu_subtype. +// */ +// #define CPU_SUBTYPE_MASK 0xff000000 /* mask for feature flags */ +// #define CPU_SUBTYPE_LIB64 0x80000000 /* 64 bit libraries */ + +// /* +// * Object files that are hand-crafted to run on any +// * implementation of an architecture are tagged with +// * CPU_SUBTYPE_MULTIPLE. This functions essentially the same as +// * the "ALL" subtype of an architecture except that it allows us +// * to easily find object files that may need to be modified +// * whenever a new implementation of an architecture comes out. +// * +// * It is the responsibility of the implementor to make sure the +// * software handles unsupported implementations elegantly. +// */ +// #define CPU_SUBTYPE_MULTIPLE -1 +// #define CPU_SUBTYPE_LITTLE_ENDIAN 0 +// #define CPU_SUBTYPE_BIG_ENDIAN 1 + +// /* +// * I386 subtypes +// */ + +// #define CPU_SUBTYPE_INTEL(f, m) ((f) + ((m) << 4)) + +// #define CPU_SUBTYPE_I386_ALL CPU_SUBTYPE_INTEL(3, 0) +// #define CPU_SUBTYPE_386 CPU_SUBTYPE_INTEL(3, 0) +// #define CPU_SUBTYPE_486 CPU_SUBTYPE_INTEL(4, 0) +// #define CPU_SUBTYPE_486SX CPU_SUBTYPE_INTEL(4, 8) // 8 << 4 = 128 +// #define CPU_SUBTYPE_586 CPU_SUBTYPE_INTEL(5, 0) +// #define CPU_SUBTYPE_PENT CPU_SUBTYPE_INTEL(5, 0) +// #define CPU_SUBTYPE_PENTPRO CPU_SUBTYPE_INTEL(6, 1) +// #define CPU_SUBTYPE_PENTII_M3 CPU_SUBTYPE_INTEL(6, 3) +// #define CPU_SUBTYPE_PENTII_M5 CPU_SUBTYPE_INTEL(6, 5) +// #define CPU_SUBTYPE_CELERON CPU_SUBTYPE_INTEL(7, 6) +// #define CPU_SUBTYPE_CELERON_MOBILE CPU_SUBTYPE_INTEL(7, 7) +// #define CPU_SUBTYPE_PENTIUM_3 CPU_SUBTYPE_INTEL(8, 0) +// #define CPU_SUBTYPE_PENTIUM_3_M CPU_SUBTYPE_INTEL(8, 1) +// #define CPU_SUBTYPE_PENTIUM_3_XEON CPU_SUBTYPE_INTEL(8, 2) +// #define CPU_SUBTYPE_PENTIUM_M CPU_SUBTYPE_INTEL(9, 0) +// #define CPU_SUBTYPE_PENTIUM_4 CPU_SUBTYPE_INTEL(10, 0) +// #define CPU_SUBTYPE_PENTIUM_4_M CPU_SUBTYPE_INTEL(10, 1) +// #define CPU_SUBTYPE_ITANIUM CPU_SUBTYPE_INTEL(11, 0) +// #define CPU_SUBTYPE_ITANIUM_2 CPU_SUBTYPE_INTEL(11, 1) +// #define CPU_SUBTYPE_XEON CPU_SUBTYPE_INTEL(12, 0) +// #define CPU_SUBTYPE_XEON_MP CPU_SUBTYPE_INTEL(12, 1) + +// #define CPU_SUBTYPE_INTEL_FAMILY(x) ((x) & 15) +// #define CPU_SUBTYPE_INTEL_FAMILY_MAX 15 + +// #define CPU_SUBTYPE_INTEL_MODEL(x) ((x) >> 4) +// #define CPU_SUBTYPE_INTEL_MODEL_ALL 0 + +// /* +// * X86 subtypes. +// */ + +// #define CPU_SUBTYPE_X86_ALL 3 +// #define CPU_SUBTYPE_X86_64_ALL 3 +// #define CPU_SUBTYPE_X86_ARCH1 4 +// #define CPU_SUBTYPE_X86_64_H 8 + +// #define CPU_SUBTYPE_ARM_ALL 0 +// #define CPU_SUBTYPE_ARM_A500_ARCH 1 +// #define CPU_SUBTYPE_ARM_A500 2 +// #define CPU_SUBTYPE_ARM_A440 3 +// #define CPU_SUBTYPE_ARM_M4 4 +// #define CPU_SUBTYPE_ARM_V4T 5 +// #define CPU_SUBTYPE_ARM_V6 6 +// #define CPU_SUBTYPE_ARM_V5 7 +// #define CPU_SUBTYPE_ARM_V5TEJ 7 +// #define CPU_SUBTYPE_ARM_XSCALE 8 +// #define CPU_SUBTYPE_ARM_V7 9 +// #define CPU_SUBTYPE_ARM_V7S 11 +// #define CPU_SUBTYPE_ARM_V7K 12 +// #define CPU_SUBTYPE_ARM_V8 13 +// #define CPU_SUBTYPE_ARM_V6M 14 +// #define CPU_SUBTYPE_ARM_V7M 15 +// #define CPU_SUBTYPE_ARM_V7EM 16 + +// #define CPU_SUBTYPE_ARM64_ALL 0 +// #define CPU_SUBTYPE_ARM64_V8 1 +// #define CPU_SUBTYPE_ARM64E 2 +// #define CPU_SUBTYPE_ARM64_32_V8 1 + + +#define FAT_MAGIC 0xcafebabe +#define FAT_CIGAM 0xbebafeca + +#define MH_MAGIC 0xfeedface +#define MH_CIGAM 0xcefaedfe +#define MH_MAGIC_64 0xfeedfacf +#define MH_CIGAM_64 0xcffaedfe + + +/* Constants for the cmd field of new load commands, the type */ + +#define MH_OBJECT 0x1 /* relocatable object file */ +#define MH_EXECUTE 0x2 /* demand paged executable file */ +#define MH_FVMLIB 0x3 /* fixed VM shared library file */ +#define MH_CORE 0x4 /* core file */ +#define MH_PRELOAD 0x5 /* preloaded executable file */ +#define MH_DYLIB 0x6 /* dynamicly bound shared library file*/ +#define MH_DYLINKER 0x7 /* dynamic link editor */ +#define MH_BUNDLE 0x8 /* dynamicly bound bundle file */ +#define MH_DYLIB_STUB 0x9 // Dynamic shared library stub +#define MH_DSYM 0xa // Companion debug sections file +#define MH_KEXT_BUNDLE 0xb // Kernel extension + +/* Constants for the flags field of the mach_header */ +#define MH_NOUNDEFS 0x00000001 /* the object file has no undefined references, can be executed */ +#define MH_INCRLINK 0x00000002 /* the object file is the output of an incremental link against a base file and can't be link edited again */ +#define MH_DYLDLINK 0x00000004 /* the object file is input for the dynamic linker and can't be staticly link edited again */ +#define MH_BINDATLOAD 0x00000008 /* the object file's undefined references are bound by the dynamic linker when loaded. */ +#define MH_PREBOUND 0x00000010 /* the file has it's dynamic undefined references prebound. */ +#define MH_SPLIT_SEGS 0x00000020 +#define MH_LAZY_INIT 0x00000040 +#define MH_TWOLEVEL 0x00000080 +#define MH_FORCE_FLAT 0x00000100 +#define MH_NOMULTIDEFS 0x00000200 +#define MH_NOFIXPREBINDING 0x00000400 +#define MH_PREBINDABLE 0x00000800 +#define MH_ALLMODSBOUND 0x00001000 +#define MH_SUBSECTIONS_VIA_SYMBOLS 0x00002000 +#define MH_CANONICAL 0x00004000 +#define MH_WEAK_DEFINES 0x00008000 +#define MH_BINDS_TO_WEAK 0x00010000 +#define MH_ALLOW_STACK_EXECUTION 0x00020000 +#define MH_ROOT_SAFE 0x00040000 +#define MH_SETUID_SAFE 0x00080000 +#define MH_NO_REEXPORTED_DYLIBS 0x00100000 +#define MH_PIE 0x00200000 +#define MH_DEAD_STRIPPABLE_DYLIB 0x00400000 +#define MH_HAS_TLV_DESCRIPTORS 0x00800000 +#define MH_NO_HEAP_EXECUTION 0x01000000 +#define MH_APP_EXTENSION_SAFE 0x02000000 + + +/* Constants for the cmd field of all load commands, the type */ +#define LC_SEGMENT 0x00000001 /* segment of this file to be mapped */ +#define LC_SYMTAB 0x00000002 /* link-edit stab symbol table info */ +#define LC_SYMSEG 0x00000003 /* link-edit gdb symbol table info (obsolete) */ +#define LC_THREAD 0x00000004 /* thread */ +#define LC_UNIXTHREAD 0x00000005 /* unix thread (includes a stack) */ +#define LC_LOADFVMLIB 0x00000006 /* load a specified fixed VM shared library */ +#define LC_IDFVMLIB 0x00000007 /* fixed VM shared library identification */ +#define LC_IDENT 0x00000008 /* object identification info (obsolete) */ +#define LC_FVMFILE 0x00000009 /* fixed VM file inclusion (internal use) */ +#define LC_PREPAGE 0x0000000a /* prepage command (internal use) */ +#define LC_DYSYMTAB 0x0000000b /* dynamic link-edit symbol table info */ +#define LC_LOAD_DYLIB 0x0000000c /* load a dynamicly linked shared library */ +#define LC_ID_DYLIB 0x0000000d /* dynamicly linked shared lib identification */ +#define LC_LOAD_DYLINKER 0x0000000e /* load a dynamic linker */ +#define LC_ID_DYLINKER 0x0000000f /* dynamic linker identification */ +#define LC_PREBOUND_DYLIB 0x00000010 /* modules prebound for a dynamicly */ +#define LC_ROUTINES 0x00000011 +#define LC_SUB_FRAMEWORK 0x00000012 +#define LC_SUB_UMBRELLA 0x00000013 +#define LC_SUB_CLIENT 0x00000014 +#define LC_SUB_LIBRARY 0x00000015 +#define LC_TWOLEVEL_HINTS 0x00000016 +#define LC_PREBIND_CKSUM 0x00000017 +#define LC_LOAD_WEAK_DYLIB 0x80000018 +#define LC_SEGMENT_64 0x00000019 +#define LC_ROUTINES_64 0x0000001A +#define LC_UUID 0x0000001B +#define LC_RPATH 0x8000001C +#define LC_CODE_SIGNATURE 0x0000001D +#define LC_SEGMENT_SPLIT_INFO 0x0000001E +#define LC_REEXPORT_DYLIB 0x8000001F +#define LC_LAZY_LOAD_DYLIB 0x00000020 +#define LC_ENCRYPTION_INFO 0x00000021 +#define LC_DYLD_INFO 0x00000022 +#define LC_DYLD_INFO_ONLY 0x80000022 +#define LC_LOAD_UPWARD_DYLIB 0x80000023 +#define LC_VERSION_MIN_MACOSX 0x00000024 +#define LC_VERSION_MIN_IPHONEOS 0x00000025 +#define LC_FUNCTION_STARTS 0x00000026 +#define LC_DYLD_ENVIRONMENT 0x00000027 +#define LC_MAIN 0x80000028 +#define LC_DATA_IN_CODE 0x00000029 +#define LC_SOURCE_VERSION 0x0000002A +#define LC_DYLIB_CODE_SIGN_DRS 0x0000002B +#define LC_ENCRYPTION_INFO_64 0x0000002C +#define LC_LINKER_OPTION 0x0000002D +#define LC_LINKER_OPTIMIZATION_HINT 0x0000002E +#define LC_VERSION_MIN_TVOS 0x0000002F +#define LC_VERSION_MIN_WATCHOS 0x00000030 + +// /* Constants for the flags field of the segment_command */ +// #define SG_HIGHVM 0x00000001 /* the file contents for this segment is for +// the high part of the VM space, the low part +// is zero filled (for stacks in core files) */ +// #define SG_FVMLIB 0x00000002 /* this segment is the VM that is allocated by +// a fixed VM library, for overlap checking in +// the link editor */ +// #define SG_NORELOC 0x00000004 /* this segment has nothing that was relocated +// in it and nothing relocated to it, that is +// it maybe safely replaced without relocation*/ +// #define SG_PROTECTED_VERSION_1 0x00000008 // Segment is encryption protected + + +// // Section flag masks +// #define SECTION_TYPE 0x000000ff // Section type mask +// #define SECTION_ATTRIBUTES 0xffffff00 // Section attributes mask + +// // Section type (use SECTION_TYPE mask) + +// #define S_REGULAR 0x00 +// #define S_ZEROFILL 0x01 +// #define S_CSTRING_LITERALS 0x02 +// #define S_4BYTE_LITERALS 0x03 +// #define S_8BYTE_LITERALS 0x04 +// #define S_LITERAL_POINTERS 0x05 +// #define S_NON_LAZY_SYMBOL_POINTERS 0x06 +// #define S_LAZY_SYMBOL_POINTERS 0x07 +// #define S_SYMBOL_STUBS 0x08 +// #define S_MOD_INIT_FUNC_POINTERS 0x09 +// #define S_MOD_TERM_FUNC_POINTERS 0x0a +// #define S_COALESCED 0x0b +// #define S_GB_ZEROFILL 0x0c +// #define S_INTERPOSING 0x0d +// #define S_16BYTE_LITERALS 0x0e +// #define S_DTRACE_DOF 0x0f +// #define S_LAZY_DYLIB_SYMBOL_POINTERS 0x10 +// #define S_THREAD_LOCAL_REGULAR 0x11 +// #define S_THREAD_LOCAL_ZEROFILL 0x12 +// #define S_THREAD_LOCAL_VARIABLES 0x13 +// #define S_THREAD_LOCAL_VARIABLE_POINTERS 0x14 +// #define S_THREAD_LOCAL_INIT_FUNCTION_POINTERS 0x15 + +// // Section attributes (use SECTION_ATTRIBUTES mask) + +// #define S_ATTR_PURE_INSTRUCTIONS 0x80000000 // Only pure instructions +// #define S_ATTR_NO_TOC 0x40000000 // Contains coalesced symbols +// #define S_ATTR_STRIP_STATIC_SYMS 0x20000000 // Can strip static symbols +// #define S_ATTR_NO_DEAD_STRIP 0x10000000 // No dead stripping +// #define S_ATTR_LIVE_SUPPORT 0x08000000 // Live blocks support +// #define S_ATTR_SELF_MODIFYING_CODE 0x04000000 // Self modifying code +// #define S_ATTR_DEBUG 0x02000000 // Debug section +// #define S_ATTR_SOME_INSTRUCTIONS 0x00000400 // Some machine instructions +// #define S_ATTR_EXT_RELOC 0x00000200 // Has external relocations +// #define S_ATTR_LOC_RELOC 0x00000100 // Has local relocations + + +//struct define + +#pragma pack(push, 1) + +struct fat_header +{ + uint32_t magic; /* FAT_MAGIC */ + uint32_t nfat_arch; /* number of structs that follow */ +}; + +struct fat_arch +{ + cpu_type_t cputype; /* cpu specifier (int) */ + cpu_subtype_t cpusubtype; /* machine specifier (int) */ + uint32_t offset; /* file offset to this object file */ + uint32_t size; /* size of this object file */ + uint32_t align; /* alignment as a power of 2 */ +}; + +struct mach_header +{ + uint32_t magic; /* mach magic number identifier */ + cpu_type_t cputype; /* cpu specifier */ + cpu_subtype_t cpusubtype; /* machine specifier */ + uint32_t filetype; /* type of file */ + uint32_t ncmds; /* number of load commands */ + uint32_t sizeofcmds; /* the size of all the load commands */ + uint32_t flags; /* flags */ +}; + +struct mach_header_64 +{ + uint32_t magic; /* mach magic number identifier */ + cpu_type_t cputype; /* cpu specifier */ + cpu_subtype_t cpusubtype; /* machine specifier */ + uint32_t filetype; /* type of file */ + uint32_t ncmds; /* number of load commands */ + uint32_t sizeofcmds; /* the size of all the load commands */ + uint32_t flags; /* flags */ + uint32_t reserved; /* reserved */ +}; + +struct load_command +{ + uint32_t cmd; /* type of load command */ + uint32_t cmdsize; /* total size of command in bytes */ +}; + +struct uuid_command { + uint32_t cmd; + uint32_t cmdsize; + uint8_t uuid[16]; +}; + +struct entry_point_command { + uint32_t cmd; + uint32_t cmdsize; + uint64_t entryoff; + uint64_t stacksize; +}; + +struct codesignature_command { + uint32_t cmd; + uint32_t cmdsize; + uint32_t dataoff; + uint32_t datasize; +}; + +struct encryption_info_command { + uint32_t cmd; + uint32_t cmdsize; + uint32_t cryptoff; + uint32_t cryptsize; + uint32_t cryptid; +}; + +struct encryption_info_command_64 +{ + uint32_t cmd; + uint32_t cmdsize; + uint32_t cryptoff; + uint32_t cryptsize; + uint32_t cryptid; + uint32_t pad; +}; + +struct segment_command { /* for 32-bit architectures */ + uint32_t cmd; /* LC_SEGMENT */ + uint32_t cmdsize; /* includes sizeof section structs */ + char segname[16]; /* segment name */ + uint32_t vmaddr; /* memory address of this segment */ + uint32_t vmsize; /* memory size of this segment */ + uint32_t fileoff; /* file offset of this segment */ + uint32_t filesize; /* amount to map from the file */ + vm_prot_t maxprot; /* maximum VM protection */ + vm_prot_t initprot; /* initial VM protection */ + uint32_t nsects; /* number of sections in segment */ + uint32_t flags; /* flags */ +}; + +struct segment_command_64 { /* for 64-bit architectures */ + uint32_t cmd; /* LC_SEGMENT_64 */ + uint32_t cmdsize; /* includes sizeof section_64 structs */ + char segname[16]; /* segment name */ + uint64_t vmaddr; /* memory address of this segment */ + uint64_t vmsize; /* memory size of this segment */ + uint64_t fileoff; /* file offset of this segment */ + uint64_t filesize; /* amount to map from the file */ + vm_prot_t maxprot; /* maximum VM protection */ + vm_prot_t initprot; /* initial VM protection */ + uint32_t nsects; /* number of sections in segment */ + uint32_t flags; /* flags */ +}; + +struct section { /* for 32-bit architectures */ + char sectname[16]; /* name of this section */ + char segname[16]; /* segment this section goes in */ + uint32_t addr; /* memory address of this section */ + uint32_t size; /* size in bytes of this section */ + uint32_t offset; /* file offset of this section */ + uint32_t align; /* section alignment (power of 2) */ + uint32_t reloff; /* file offset of relocation entries */ + uint32_t nreloc; /* number of relocation entries */ + uint32_t flags; /* flags (section type and attributes)*/ + uint32_t reserved1; /* reserved */ + uint32_t reserved2; /* reserved */ +}; + +struct section_64 { /* for 64-bit architectures */ + char sectname[16]; /* name of this section */ + char segname[16]; /* segment this section goes in */ + uint64_t addr; /* memory address of this section */ + uint64_t size; /* size in bytes of this section */ + uint32_t offset; /* file offset of this section */ + uint32_t align; /* section alignment (power of 2) */ + uint32_t reloff; /* file offset of relocation entries */ + uint32_t nreloc; /* number of relocation entries */ + uint32_t flags; /* flags (section type and attributes)*/ + uint32_t reserved1; /* reserved (for offset or index) */ + uint32_t reserved2; /* reserved (for count or sizeof) */ + uint32_t reserved3; /* reserved */ +}; + +union lc_str { + uint32_t offset; /* offset to the string */ +}; + +struct dylib { + union lc_str name; /* library's path name */ + uint32_t timestamp; /* library's build time stamp */ + uint32_t current_version; /* library's current version number */ + uint32_t compatibility_version; /* library's compatibility vers number*/ +}; + +struct dylib_command { + uint32_t cmd; /* LC_ID_DYLIB, LC_LOAD_{,WEAK_}DYLIB,LC_REEXPORT_DYLIB */ + uint32_t cmdsize; /* includes pathname string */ + struct dylib dylib; /* the library identification */ +}; + +#pragma pack(pop) + +//////CodeSignature + +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_EMBEDDED_SIGNATURE_OLD = 0xfade0b02, /* XXX */ + CSMAGIC_EMBEDDED_ENTITLEMENTS = 0xfade7171, /* embedded entitlements */ + CSMAGIC_EMBEDDED_DER_ENTITLEMENTS = 0xfade7172, /* der format entitlements */ + CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1, /* multi-arch collection of embedded signatures */ + CSMAGIC_BLOBWRAPPER = 0xfade0b01, /* CMS Signature, among other things */ + CS_SUPPORTSSCATTER = 0x20100, + CS_SUPPORTSTEAMID = 0x20200, + CS_SUPPORTSCODELIMIT64 = 0x20300, + CS_SUPPORTSEXECSEG = 0x20400, + CSSLOT_CODEDIRECTORY = 0, /* slot index for CodeDirectory */ + CSSLOT_INFOSLOT = 1, + CSSLOT_REQUIREMENTS = 2, + CSSLOT_RESOURCEDIR = 3, + CSSLOT_APPLICATION = 4, + CSSLOT_ENTITLEMENTS = 5, + CSSLOT_DER_ENTITLEMENTS = 7, /* der format entitlement type */ + CSSLOT_ALTERNATE_CODEDIRECTORIES = 0x1000, /* first alternate CodeDirectory, if any */ + CSSLOT_ALTERNATE_CODEDIRECTORY_MAX = 5, /* max number of alternate CD slots */ + CSSLOT_ALTERNATE_CODEDIRECTORY_LIMIT = CSSLOT_ALTERNATE_CODEDIRECTORIES + CSSLOT_ALTERNATE_CODEDIRECTORY_MAX, /* one past the last */ + CSSLOT_SIGNATURESLOT = 0x10000, /* CMS Signature */ + CSSLOT_IDENTIFICATIONSLOT = 0x10001, + CSSLOT_TICKETSLOT = 0x10002, + CSTYPE_INDEX_REQUIREMENTS = 0x00000002, /* compat with amfi */ + CSTYPE_INDEX_ENTITLEMENTS = 0x00000005, /* compat with amfi */ + CS_HASHTYPE_SHA1 = 1, + CS_HASHTYPE_SHA256 = 2, + CS_HASHTYPE_SHA256_TRUNCATED = 3, + CS_HASHTYPE_SHA384 = 4, + CS_SHA1_LEN = 20, + CS_SHA256_LEN = 32, + CS_SHA256_TRUNCATED_LEN = 20, + CS_CDHASH_LEN = 20, /* always - larger hashes are truncated */ + CS_HASH_MAX_SIZE = 48, /* max size of the hash we'll support */ + CS_EXECSEG_MAIN_BINARY = 0x1, + CS_EXECSEG_ALLOW_UNSIGNED = 0x10, + +/* + * Currently only to support Legacy VPN plugins, + * but intended to replace all the various platform code, dev code etc. bits. + */ + CS_SIGNER_TYPE_UNKNOWN = 0, + CS_SIGNER_TYPE_LEGACYVPN = 5, + +}; + +#pragma pack(push, 1) + +/* + * Structure of an embedded-signature SuperBlob + */ +struct CS_BlobIndex { + uint32_t type; /* type of entry */ + uint32_t offset; /* offset of entry */ +}; + +struct CS_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 */ +}; + +/* + * C form of a CodeDirectory. + */ +struct CS_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) */ + //char end_earliest[0]; + + /* Version 0x20100 */ + uint32_t scatterOffset; /* offset of optional scatter vector */ + //char end_withScatter[0]; + + /* Version 0x20200 */ + uint32_t teamOffset; /* offset of optional team identifier */ + //char end_withTeam[0]; + + /* Version 0x20300 */ + uint32_t spare3; /* unused (must be zero) */ + uint64_t codeLimit64; /* limit to main image signature range, 64 bits */ + //char end_withCodeLimit64[0]; + + /* Version 0x20400 */ + uint64_t execSegBase; /* offset of executable segment */ + uint64_t execSegLimit; /* limit of executable segment */ + uint64_t execSegFlags; /* executable segment flags */ + //char end_withExecSeg[0]; + + /* followed by dynamic content as located by offset fields above */ +}; + +struct CS_Entitlement { + uint32_t magic; + uint32_t length; +}; + +struct CS_GenericBlob { + uint32_t magic; /* magic number */ + uint32_t length; /* total length of blob */ +}; + +struct CS_Scatter { + uint32_t count; // number of pages; zero for sentinel (only) + uint32_t base; // first page number + uint64_t targetOffset; // offset in target + uint64_t spare; // reserved +}; + +#pragma pack(pop) diff --git a/ZSign/macho.cpp b/ZSign/macho.cpp new file mode 100644 index 0000000..745b132 --- /dev/null +++ b/ZSign/macho.cpp @@ -0,0 +1,350 @@ +#include "common/common.h" +#include "common/json.h" +#include "common/mach-o.h" +#include "openssl.h" +#include "signing.h" +#include "macho.h" + +ZMachO::ZMachO() +{ + m_pBase = NULL; + m_sSize = 0; + m_bCSRealloced = false; +} + +ZMachO::~ZMachO() +{ + FreeArchOes(); +} + +bool ZMachO::Init(const char *szFile) +{ + m_strFile = szFile; + return OpenFile(szFile); +} + +bool ZMachO::InitV(const char *szFormatPath, ...) +{ + char szFile[PATH_MAX] = {0}; + va_list args; + va_start(args, szFormatPath); + vsnprintf(szFile, PATH_MAX, szFormatPath, args); + va_end(args); + + return Init(szFile); +} + +bool ZMachO::Free() +{ + FreeArchOes(); + return CloseFile(); +} + +bool ZMachO::NewArchO(uint8_t *pBase, uint32_t uLength) +{ + ZArchO *archo = new ZArchO(); + if (archo->Init(pBase, uLength)) + { + m_arrArchOes.push_back(archo); + return true; + } + delete archo; + return false; +} + +void ZMachO::FreeArchOes() +{ + for (size_t i = 0; i < m_arrArchOes.size(); i++) + { + ZArchO *archo = m_arrArchOes[i]; + delete archo; + } + m_pBase = NULL; + m_sSize = 0; + m_arrArchOes.clear(); +} + +bool ZMachO::OpenFile(const char *szPath) +{ + FreeArchOes(); + + m_sSize = 0; + m_pBase = (uint8_t *)MapFile(szPath, 0, 0, &m_sSize, false); + if (NULL != m_pBase) + { + uint32_t magic = *((uint32_t *)m_pBase); + if (FAT_CIGAM == magic || FAT_MAGIC == magic) + { + fat_header *pFatHeader = (fat_header *)m_pBase; + int nFatArch = (FAT_MAGIC == magic) ? pFatHeader->nfat_arch : LE(pFatHeader->nfat_arch); + for (int i = 0; i < nFatArch; i++) + { + fat_arch *pFatArch = (fat_arch *)(m_pBase + sizeof(fat_header) + sizeof(fat_arch) * i); + uint8_t *pArchBase = m_pBase + ((FAT_MAGIC == magic) ? pFatArch->offset : LE(pFatArch->offset)); + uint32_t uArchLength = (FAT_MAGIC == magic) ? pFatArch->size : LE(pFatArch->size); + if (!NewArchO(pArchBase, uArchLength)) + { + ZLog::ErrorV(">>> Invalid Arch File In Fat Macho File!\n"); + return false; + } + } + } + else if (MH_MAGIC == magic || MH_CIGAM == magic || MH_MAGIC_64 == magic || MH_CIGAM_64 == magic) + { + if (!NewArchO(m_pBase, (uint32_t)m_sSize)) + { + ZLog::ErrorV(">>> Invalid Macho File!\n"); + return false; + } + } + else + { + ZLog::ErrorV(">>> Invalid Macho File (2)!\n"); + return false; + } + } + + return (!m_arrArchOes.empty()); +} + +bool ZMachO::CloseFile() +{ + if (NULL == m_pBase || m_sSize <= 0) + { + return false; + } + + if ((munmap((void *)m_pBase, m_sSize)) < 0) + { + ZLog::ErrorV(">>> CodeSign Write(munmap) Failed! Error: %p, %lu, %s\n", m_pBase, m_sSize, strerror(errno)); + return false; + } + return true; +} + +void ZMachO::PrintInfo() +{ + for (size_t i = 0; i < m_arrArchOes.size(); i++) + { + ZArchO *archo = m_arrArchOes[i]; + archo->PrintInfo(); + } +} + +bool ZMachO::Sign(ZSignAsset *pSignAsset, bool bForce, string strBundleId, string strInfoPlistSHA1, string strInfoPlistSHA256, const string &strCodeResourcesData) +{ + if (NULL == m_pBase || m_arrArchOes.empty()) + { + return false; + } + + for (size_t i = 0; i < m_arrArchOes.size(); i++) + { + ZArchO *archo = m_arrArchOes[i]; + if (strBundleId.empty()) + { + JValue jvInfo; + jvInfo.readPList(archo->m_strInfoPlist); + strBundleId = jvInfo["CFBundleIdentifier"].asCString(); + if (strBundleId.empty()) + { + strBundleId = basename((char *)m_strFile.c_str()); + } + } + + if (strInfoPlistSHA1.empty() || strInfoPlistSHA256.empty()) + { + if (archo->m_strInfoPlist.empty()) + { + strInfoPlistSHA1.append(20, 0); + strInfoPlistSHA256.append(32, 0); + } + else + { + SHASum(archo->m_strInfoPlist, strInfoPlistSHA1, strInfoPlistSHA256); + } + } + + if (!archo->Sign(pSignAsset, bForce, strBundleId, strInfoPlistSHA1, strInfoPlistSHA256, strCodeResourcesData)) + { + if (!archo->m_bEnoughSpace && !m_bCSRealloced) + { + m_bCSRealloced = true; + if (ReallocCodeSignSpace()) + { + return Sign(pSignAsset, bForce, strBundleId, strInfoPlistSHA1, strInfoPlistSHA256, strCodeResourcesData); + } + } + return false; + } + } + + return CloseFile(); +} + +bool ZMachO::ReallocCodeSignSpace() +{ + ZLog::Warn(">>> Realloc CodeSignature Space... \n"); + + vector arrMachOesSizes; + for (size_t i = 0; i < m_arrArchOes.size(); i++) + { + string strNewArchOFile; + StringFormat(strNewArchOFile, "%s.archo.%d", m_strFile.c_str(), i); + uint32_t uNewLength = m_arrArchOes[i]->ReallocCodeSignSpace(strNewArchOFile); + if (uNewLength <= 0) + { + ZLog::Error(">>> Failed!\n"); + return false; + } + arrMachOesSizes.push_back(uNewLength); + } + ZLog::Warn(">>> Success!\n"); + + if (1 == m_arrArchOes.size()) + { + CloseFile(); + RemoveFile(m_strFile.c_str()); + string strNewArchOFile = m_strFile + ".archo.0"; + if (0 == rename(strNewArchOFile.c_str(), m_strFile.c_str())) + { + return OpenFile(m_strFile.c_str()); + } + } + else + { //fat + uint32_t uAlign = 16384; + vector arrArches; + fat_header fath = *((fat_header *)m_pBase); + int nFatArch = (FAT_MAGIC == fath.magic) ? fath.nfat_arch : LE(fath.nfat_arch); + for (int i = 0; i < nFatArch; i++) + { + fat_arch arch = *((fat_arch *)(m_pBase + sizeof(fat_header) + sizeof(fat_arch) * i)); + arrArches.push_back(arch); + } + CloseFile(); + + if (arrArches.size() != m_arrArchOes.size()) + { + return false; + } + + uint32_t uFatHeaderSize = sizeof(fat_header) + arrArches.size() * sizeof(fat_arch); + uint32_t uPadding1 = (uAlign - uFatHeaderSize % uAlign); + uint32_t uOffset = uFatHeaderSize + uPadding1; + for (size_t i = 0; i < arrArches.size(); i++) + { + fat_arch &arch = arrArches[i]; + uint32_t &uMachOSize = arrMachOesSizes[i]; + + arch.align = (FAT_MAGIC == fath.magic) ? 14 : BE((uint32_t)14); + arch.offset = (FAT_MAGIC == fath.magic) ? uOffset : BE(uOffset); + arch.size = (FAT_MAGIC == fath.magic) ? uMachOSize : BE(uMachOSize); + + uOffset += uMachOSize; + uOffset = uOffset + (uAlign - uOffset % uAlign); + } + + string strNewFatMachOFile = m_strFile + ".fato"; + + string strFatHeader; + strFatHeader.append((const char *)&fath, sizeof(fat_header)); + for (size_t i = 0; i < arrArches.size(); i++) + { + fat_arch &arch = arrArches[i]; + strFatHeader.append((const char *)&arch, sizeof(fat_arch)); + } + + string strPadding1; + strPadding1.append(uPadding1, 0); + + AppendFile(strNewFatMachOFile.c_str(), strFatHeader); + AppendFile(strNewFatMachOFile.c_str(), strPadding1); + + for (size_t i = 0; i < arrArches.size(); i++) + { + size_t sSize = 0; + string strNewArchOFile = m_strFile + ".archo." + JValue((int)i).asString(); + uint8_t *pData = (uint8_t *)MapFile(strNewArchOFile.c_str(), 0, 0, &sSize, true); + if (NULL == pData) + { + RemoveFile(strNewFatMachOFile.c_str()); + return false; + } + string strPadding; + strPadding.append((uAlign - sSize % uAlign), 0); + + AppendFile(strNewFatMachOFile.c_str(), (const char *)pData, sSize); + AppendFile(strNewFatMachOFile.c_str(), strPadding); + + munmap((void *)pData, sSize); + RemoveFile(strNewArchOFile.c_str()); + } + + RemoveFile(m_strFile.c_str()); + if (0 == rename(strNewFatMachOFile.c_str(), m_strFile.c_str())) + { + return OpenFile(m_strFile.c_str()); + } + } + + return false; +} + +bool ZMachO::InjectDyLib(bool bWeakInject, const char *szDyLibPath, bool &bCreate) +{ + ZLog::WarnV(">>> Inject DyLib: %s ... \n", szDyLibPath); + + vector arrMachOesSizes; + for (size_t i = 0; i < m_arrArchOes.size(); i++) + { + if (!m_arrArchOes[i]->InjectDyLib(bWeakInject, szDyLibPath, bCreate)) + { + ZLog::Error(">>> Failed!\n"); + return false; + } + } + ZLog::Warn(">>> Success!\n"); + return true; +} + +bool ZMachO::ChangeDylibPath(const char *oldPath, const char *newPath) { + ZLog::WarnV(">>> Change DyLib Path: %s -> %s ... \n", oldPath, newPath); + + bool pathChanged = true; + for (size_t i = 0; i < m_arrArchOes.size(); i++) { + if (!m_arrArchOes[i]->ChangeDylibPath(oldPath, newPath)) { + ZLog::Error(">>> Failed to change path in one of the architectures!\n"); + pathChanged = false; + } + } + + if (pathChanged) { + ZLog::Warn(">>> Successfully changed all dylib paths!\n"); + } + return pathChanged; +} + +std::vector ZMachO::ListDylibs() { + std::vector dylibList; + + for (size_t i = 0; i < m_arrArchOes.size(); i++) { + std::vector archDylibs = m_arrArchOes[i]->ListDylibs(); + dylibList.insert(dylibList.end(), archDylibs.begin(), archDylibs.end()); + } + + ZLog::WarnV(">>> Found %zu dylibs:\n", dylibList.size()); + + return dylibList; +} +bool ZMachO::RemoveDylib(const std::set &dylibNames) { + ZLog::Warn(">>> Removing specified dylibs...\n"); + + bool removalSuccessful = true; + for (size_t i = 0; i < m_arrArchOes.size(); i++) { + m_arrArchOes[i]->uninstallDylibs(dylibNames); + } + + ZLog::Warn(">>> Finished removing specified dylibs!\n"); + return removalSuccessful; +} diff --git a/ZSign/macho.h b/ZSign/macho.h new file mode 100644 index 0000000..e8bf3da --- /dev/null +++ b/ZSign/macho.h @@ -0,0 +1,34 @@ +#pragma once +#include "archo.h" + +class ZMachO +{ +public: + ZMachO(); + ~ZMachO(); + +public: + bool Init(const char *szFile); + bool InitV(const char *szFormatPath, ...); + bool Free(); + void PrintInfo(); + bool Sign(ZSignAsset *pSignAsset, bool bForce, string strBundleId, string strInfoPlistSHA1, string strInfoPlistSHA256, const string &strCodeResourcesData); + bool InjectDyLib(bool bWeakInject, const char *szDyLibPath, bool &bCreate); + bool ChangeDylibPath(const char *oldPath, const char *newPath); + std::vector ListDylibs(); + bool RemoveDylib(const std::set &dylibNames); +private: + bool OpenFile(const char *szPath); + bool CloseFile(); + + bool NewArchO(uint8_t *pBase, uint32_t uLength); + void FreeArchOes(); + bool ReallocCodeSignSpace(); + +private: + size_t m_sSize; + string m_strFile; + uint8_t *m_pBase; + bool m_bCSRealloced; + vector m_arrArchOes; +}; diff --git a/ZSign/openssl.cpp b/ZSign/openssl.cpp new file mode 100644 index 0000000..7147b06 --- /dev/null +++ b/ZSign/openssl.cpp @@ -0,0 +1,944 @@ +#include "common/common.h" +#include "common/base64.h" +#include "openssl.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +class COpenSSLInit +{ +public: + COpenSSLInit() + { +#if OPENSSL_VERSION_NUMBER < 0x10100000L + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); +#endif + }; +}; + +COpenSSLInit g_OpenSSLInit; + +const char *appleDevCACert = "" + "-----BEGIN CERTIFICATE-----\n" + "MIIEIjCCAwqgAwIBAgIIAd68xDltoBAwDQYJKoZIhvcNAQEFBQAwYjELMAkGA1UE\n" + "BhMCVVMxEzARBgNVBAoTCkFwcGxlIEluYy4xJjAkBgNVBAsTHUFwcGxlIENlcnRp\n" + "ZmljYXRpb24gQXV0aG9yaXR5MRYwFAYDVQQDEw1BcHBsZSBSb290IENBMB4XDTEz\n" + "MDIwNzIxNDg0N1oXDTIzMDIwNzIxNDg0N1owgZYxCzAJBgNVBAYTAlVTMRMwEQYD\n" + "VQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3JsZHdpZGUgRGV2ZWxv\n" + "cGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3aWRlIERldmVsb3Bl\n" + "ciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3\n" + "DQEBAQUAA4IBDwAwggEKAoIBAQDKOFSmy1aqyCQ5SOmM7uxfuH8mkbw0U3rOfGOA\n" + "YXdkXqUHI7Y5/lAtFVZYcC1+xG7BSoU+L/DehBqhV8mvexj/avoVEkkVCBmsqtsq\n" + "Mu2WY2hSFT2Miuy/axiV4AOsAX2XBWfODoWVN2rtCbauZ81RZJ/GXNG8V25nNYB2\n" + "NqSHgW44j9grFU57Jdhav06DwY3Sk9UacbVgnJ0zTlX5ElgMhrgWDcHld0WNUEi6\n" + "Ky3klIXh6MSdxmilsKP8Z35wugJZS3dCkTm59c3hTO/AO0iMpuUhXf1qarunFjVg\n" + "0uat80YpyejDi+l5wGphZxWy8P3laLxiX27Pmd3vG2P+kmWrAgMBAAGjgaYwgaMw\n" + "HQYDVR0OBBYEFIgnFwmpthhgi+zruvZHWcVSVKO3MA8GA1UdEwEB/wQFMAMBAf8w\n" + "HwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/CF4wLgYDVR0fBCcwJTAjoCGg\n" + "H4YdaHR0cDovL2NybC5hcHBsZS5jb20vcm9vdC5jcmwwDgYDVR0PAQH/BAQDAgGG\n" + "MBAGCiqGSIb3Y2QGAgEEAgUAMA0GCSqGSIb3DQEBBQUAA4IBAQBPz+9Zviz1smwv\n" + "j+4ThzLoBTWobot9yWkMudkXvHcs1Gfi/ZptOllc34MBvbKuKmFysa/Nw0Uwj6OD\n" + "Dc4dR7Txk4qjdJukw5hyhzs+r0ULklS5MruQGFNrCk4QttkdUGwhgAqJTleMa1s8\n" + "Pab93vcNIx0LSiaHP7qRkkykGRIZbVf1eliHe2iK5IaMSuviSRSqpd1VAKmuu0sw\n" + "ruGgsbwpgOYJd+W+NKIByn/c4grmO7i77LpilfMFY0GCzQ87HUyVpNur+cmV6U/k\n" + "TecmmYHpvPm0KdIBembhLoz2IYrF+Hjhga6/05Cdqa3zr/04GpZnMBxRpVzscYqC\n" + "tGwPDBUf\n" + "-----END CERTIFICATE-----\n"; + +const char *appleDevCACertG3 = "" + "-----BEGIN CERTIFICATE-----\n" + "MIIEUTCCAzmgAwIBAgIQfK9pCiW3Of57m0R6wXjF7jANBgkqhkiG9w0BAQsFADBi\n" + "MQswCQYDVQQGEwJVUzETMBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBw\n" + "bGUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3Qg\n" + "Q0EwHhcNMjAwMjE5MTgxMzQ3WhcNMzAwMjIwMDAwMDAwWjB1MUQwQgYDVQQDDDtB\n" + "cHBsZSBXb3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9ucyBDZXJ0aWZpY2F0aW9u\n" + "IEF1dGhvcml0eTELMAkGA1UECwwCRzMxEzARBgNVBAoMCkFwcGxlIEluYy4xCzAJ\n" + "BgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2PWJ/KhZ\n" + "C4fHTJEuLVaQ03gdpDDppUjvC0O/LYT7JF1FG+XrWTYSXFRknmxiLbTGl8rMPPbW\n" + "BpH85QKmHGq0edVny6zpPwcR4YS8Rx1mjjmi6LRJ7TrS4RBgeo6TjMrA2gzAg9Dj\n" + "+ZHWp4zIwXPirkbRYp2SqJBgN31ols2N4Pyb+ni743uvLRfdW/6AWSN1F7gSwe0b\n" + "5TTO/iK1nkmw5VW/j4SiPKi6xYaVFuQAyZ8D0MyzOhZ71gVcnetHrg21LYwOaU1A\n" + "0EtMOwSejSGxrC5DVDDOwYqGlJhL32oNP/77HK6XF8J4CjDgXx9UO0m3JQAaN4LS\n" + "VpelUkl8YDib7wIDAQABo4HvMIHsMBIGA1UdEwEB/wQIMAYBAf8CAQAwHwYDVR0j\n" + "BBgwFoAUK9BpR5R2Cf70a40uQKb3R01/CF4wRAYIKwYBBQUHAQEEODA2MDQGCCsG\n" + "AQUFBzABhihodHRwOi8vb2NzcC5hcHBsZS5jb20vb2NzcDAzLWFwcGxlcm9vdGNh\n" + "MC4GA1UdHwQnMCUwI6AhoB+GHWh0dHA6Ly9jcmwuYXBwbGUuY29tL3Jvb3QuY3Js\n" + "MB0GA1UdDgQWBBQJ/sAVkPmvZAqSErkmKGMMl+ynsjAOBgNVHQ8BAf8EBAMCAQYw\n" + "EAYKKoZIhvdjZAYCAQQCBQAwDQYJKoZIhvcNAQELBQADggEBAK1lE+j24IF3RAJH\n" + "Qr5fpTkg6mKp/cWQyXMT1Z6b0KoPjY3L7QHPbChAW8dVJEH4/M/BtSPp3Ozxb8qA\n" + "HXfCxGFJJWevD8o5Ja3T43rMMygNDi6hV0Bz+uZcrgZRKe3jhQxPYdwyFot30ETK\n" + "XXIDMUacrptAGvr04NM++i+MZp+XxFRZ79JI9AeZSWBZGcfdlNHAwWx/eCHvDOs7\n" + "bJmCS1JgOLU5gm3sUjFTvg+RTElJdI+mUcuER04ddSduvfnSXPN/wmwLCTbiZOTC\n" + "NwMUGdXqapSqqdv+9poIZ4vvK7iqF0mDr8/LvOnP6pVxsLRFoszlh6oKw0E6eVza\n" + "UDSdlTs=\n" + "-----END CERTIFICATE-----\n"; + +const char *appleRootCACert = "" + "-----BEGIN CERTIFICATE-----\n" + "MIIEuzCCA6OgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQGEwJVUzET\n" + "MBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlv\n" + "biBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwHhcNMDYwNDI1MjE0\n" + "MDM2WhcNMzUwMjA5MjE0MDM2WjBiMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQXBw\n" + "bGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkx\n" + "FjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw\n" + "ggEKAoIBAQDkkakJH5HbHkdQ6wXtXnmELes2oldMVeyLGYne+Uts9QerIjAC6Bg+\n" + "+FAJ039BqJj50cpmnCRrEdCju+QbKsMflZ56DKRHi1vUFjczy8QPTc4UadHJGXL1\n" + "XQ7Vf1+b8iUDulWPTV0N8WQ1IxVLFVkds5T39pyez1C6wVhQZ48ItCD3y6wsIG9w\n" + "tj8BMIy3Q88PnT3zK0koGsj+zrW5DtleHNbLPbU6rfQPDgCSC7EhFi501TwN22IW\n" + "q6NxkkdTVcGvL0Gz+PvjcM3mo0xFfh9Ma1CWQYnEdGILEINBhzOKgbEwWOxaBDKM\n" + "aLOPHd5lc/9nXmW8Sdh2nzMUZaF3lMktAgMBAAGjggF6MIIBdjAOBgNVHQ8BAf8E\n" + "BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUK9BpR5R2Cf70a40uQKb3\n" + "R01/CF4wHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/CF4wggERBgNVHSAE\n" + "ggEIMIIBBDCCAQAGCSqGSIb3Y2QFATCB8jAqBggrBgEFBQcCARYeaHR0cHM6Ly93\n" + "d3cuYXBwbGUuY29tL2FwcGxlY2EvMIHDBggrBgEFBQcCAjCBthqBs1JlbGlhbmNl\n" + "IG9uIHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFzc3VtZXMgYWNjZXB0\n" + "YW5jZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJkIHRlcm1zIGFuZCBj\n" + "b25kaXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5IGFuZCBjZXJ0aWZp\n" + "Y2F0aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMA0GCSqGSIb3DQEBBQUAA4IBAQBc\n" + "NplMLXi37Yyb3PN3m/J20ncwT8EfhYOFG5k9RzfyqZtAjizUsZAS2L70c5vu0mQP\n" + "y3lPNNiiPvl4/2vIB+x9OYOLUyDTOMSxv5pPCmv/K/xZpwUJfBdAVhEedNO3iyM7\n" + "R6PVbyTi69G3cN8PReEnyvFteO3ntRcXqNx+IjXKJdXZD9Zr1KIkIxH3oayPc4Fg\n" + "xhtbCS+SsvhESPBgOJ4V9T0mZyCKM2r3DYLP3uujL/lTaltkwGMzd/c6ByxW69oP\n" + "IQ7aunMZT7XZNn/Bh1XZp5m5MkL72NVxnn6hUrcbvZNCJBIqxw8dtk2cXmPIS4AX\n" + "UKqK1drk/NAJBzewdXUh\n" + "-----END CERTIFICATE-----\n"; + +bool CMSError() +{ + ERR_print_errors_fp(stdout); + return false; +} + +ASN1_TYPE *_GenerateASN1Type(const string &value) +{ + long errline = -1; + char *genstr = NULL; + BIO *ldapbio = BIO_new(BIO_s_mem()); + CONF *cnf = NCONF_new(NULL); + + if (cnf == NULL) { + ZLog::Error(">>> NCONF_new failed\n"); + BIO_free(ldapbio); + } + string a = "asn1=SEQUENCE:A\n[A]\nC=OBJECT:sha256\nB=FORMAT:HEX,OCT:" + value + "\n"; + int code = BIO_puts(ldapbio, a.c_str()); + if (NCONF_load_bio(cnf, ldapbio, &errline) <= 0) { + BIO_free(ldapbio); + NCONF_free(cnf); + ZLog::PrintV(">>> NCONF_load_bio failed %d\n", errline); + } + BIO_free(ldapbio); + genstr = NCONF_get_string(cnf, "default", "asn1"); + + if (genstr == NULL) { + ZLog::Error(">>> NCONF_get_string failed\n"); + NCONF_free(cnf); + } + ASN1_TYPE *ret = ASN1_generate_nconf(genstr, cnf); + NCONF_free(cnf); + return ret; +} + +bool _GenerateCMS(X509 *scert, EVP_PKEY *spkey, const string &strCDHashData, const string &strCDHashesPlist, const string &strCodeDirectorySlotSHA1, const string &strAltnateCodeDirectorySlot256, string &strCMSOutput) +{ + if (!scert || !spkey) + { + return CMSError(); + } + + BIO *bother1; + unsigned long issuerHash = X509_issuer_name_hash(scert); + if (0x817d2f7a == issuerHash) + { + bother1 = BIO_new_mem_buf(appleDevCACert, (int)strlen(appleDevCACert)); + } + else if (0x9b16b75c == issuerHash) + { + bother1 = BIO_new_mem_buf(appleDevCACertG3, (int)strlen(appleDevCACertG3)); + } + else + { + ZLog::Error(">>> Unknown Issuer Hash!\n"); + return false; + } + + BIO *bother2 = BIO_new_mem_buf(appleRootCACert, (int)strlen(appleRootCACert)); + if (!bother1 || !bother2) + { + return CMSError(); + } + + X509 *ocert1 = PEM_read_bio_X509(bother1, NULL, 0, NULL); + X509 *ocert2 = PEM_read_bio_X509(bother2, NULL, 0, NULL); + if (!ocert1 || !ocert2) + { + return CMSError(); + } + + STACK_OF(X509) *otherCerts = sk_X509_new_null(); + if (!otherCerts) + { + return CMSError(); + } + + if (!sk_X509_push(otherCerts, ocert1)) + { + return CMSError(); + } + + if (!sk_X509_push(otherCerts, ocert2)) + { + return CMSError(); + } + + BIO *in = BIO_new_mem_buf(strCDHashData.c_str(), (int)strCDHashData.size()); + if (!in) + { + return CMSError(); + } + + int nFlags = CMS_PARTIAL | CMS_DETACHED | CMS_NOSMIMECAP | CMS_BINARY; + CMS_ContentInfo *cms = CMS_sign(NULL, NULL, otherCerts, NULL, nFlags); + if (!cms) + { + return CMSError(); + } + + CMS_SignerInfo * si = CMS_add1_signer(cms, scert, spkey, EVP_sha256(), nFlags); +// CMS_add1_signer(cms, NULL, NULL, EVP_sha1(), nFlags); + if (!si) { + return CMSError(); + } + + // add plist + ASN1_OBJECT * obj = OBJ_txt2obj("1.2.840.113635.100.9.1", 1); + if (!obj) { + return CMSError(); + } + + int addHashPlist = CMS_signed_add1_attr_by_OBJ(si, obj, 0x4, strCDHashesPlist.c_str(), (int)strCDHashesPlist.size()); + + if (!addHashPlist) { + return CMSError(); + } + + // add CDHashes + string sha256; + char buf[16] = {0}; + for (size_t i = 0; i < strAltnateCodeDirectorySlot256.size(); i++) + { + sprintf(buf, "%02x", (uint8_t)strAltnateCodeDirectorySlot256[i]); + sha256 += buf; + } + transform(sha256.begin(), sha256.end(), sha256.begin(), ::toupper); + + ASN1_OBJECT * obj2 = OBJ_txt2obj("1.2.840.113635.100.9.2", 1); + if (!obj2) { + return CMSError(); + } + + X509_ATTRIBUTE *attr = X509_ATTRIBUTE_new(); + X509_ATTRIBUTE_set1_object(attr, obj2); + + ASN1_TYPE *type_256 = _GenerateASN1Type(sha256); + X509_ATTRIBUTE_set1_data(attr, V_ASN1_SEQUENCE, + type_256->value.asn1_string->data, type_256->value.asn1_string->length); + int addHashSHA = CMS_signed_add1_attr(si, attr); + if (!addHashSHA) { + return CMSError(); + } + + if (!CMS_final(cms, in, NULL, nFlags)) + { + return CMSError(); + } + + BIO *out = BIO_new(BIO_s_mem()); + if (!out) + { + return CMSError(); + } + + //PEM_write_bio_CMS(out, cms); + if (!i2d_CMS_bio(out, cms)) + { + return CMSError(); + } + + BUF_MEM *bptr = NULL; + BIO_get_mem_ptr(out, &bptr); + if (!bptr) + { + return CMSError(); + } + + strCMSOutput.clear(); + strCMSOutput.append(bptr->data, bptr->length); + ASN1_TYPE_free(type_256); + return (!strCMSOutput.empty()); +} + +bool GenerateCMS(const string &strSignerCertData, const string &strSignerPKeyData, const string &strCDHashData, const string &strCDHashesPlist, string &strCMSOutput) +{ + BIO *bcert = BIO_new_mem_buf(strSignerCertData.c_str(), (int)strSignerCertData.size()); + BIO *bpkey = BIO_new_mem_buf(strSignerPKeyData.c_str(), (int)strSignerPKeyData.size()); + + if (!bcert || !bpkey) + { + return CMSError(); + } + + X509 *scert = PEM_read_bio_X509(bcert, NULL, 0, NULL); + EVP_PKEY *spkey = PEM_read_bio_PrivateKey(bpkey, NULL, 0, NULL); + if (!scert || !spkey) + { + return CMSError(); + } + + return ::_GenerateCMS(scert, spkey, strCDHashData, strCDHashesPlist, "", "", strCMSOutput); +} + +bool GetCMSContent(const string &strCMSDataInput, string &strContentOutput) +{ + if (strCMSDataInput.empty()) + { + return false; + } + + BIO *in = BIO_new(BIO_s_mem()); + OPENSSL_assert((size_t)BIO_write(in, strCMSDataInput.data(), (int)strCMSDataInput.size()) == strCMSDataInput.size()); + CMS_ContentInfo *cms = d2i_CMS_bio(in, NULL); + if (!cms) + { + return CMSError(); + } + + ASN1_OCTET_STRING **pos = CMS_get0_content(cms); + if (!pos) + { + return CMSError(); + } + + if (!(*pos)) + { + return CMSError(); + } + + strContentOutput.clear(); + strContentOutput.append((const char *)(*pos)->data, (*pos)->length); + return (!strContentOutput.empty()); +} + +bool GetCMSContent2(const void* strCMSDataInput, int size, string &strContentOutput) +{ + if (size == 0) + { + return false; + } + + BIO *in = BIO_new(BIO_s_mem()); + OPENSSL_assert((size_t)BIO_write(in, strCMSDataInput, size) == size); + CMS_ContentInfo *cms = d2i_CMS_bio(in, NULL); + if (!cms) + { + return CMSError(); + } + + ASN1_OCTET_STRING **pos = CMS_get0_content(cms); + if (!pos) + { + return CMSError(); + } + + if (!(*pos)) + { + return CMSError(); + } + + strContentOutput.clear(); + strContentOutput.append((const char *)(*pos)->data, (*pos)->length); + return (!strContentOutput.empty()); +} + +bool GetCertSubjectCN(X509 *cert, string &strSubjectCN) +{ + if (!cert) + { + return CMSError(); + } + + X509_NAME *name = X509_get_subject_name(cert); + + int common_name_loc = X509_NAME_get_index_by_NID(name, NID_commonName, -1); + if (common_name_loc < 0) + { + return CMSError(); + } + + X509_NAME_ENTRY *common_name_entry = X509_NAME_get_entry(name, common_name_loc); + if (common_name_entry == NULL) + { + return CMSError(); + } + + ASN1_STRING *common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry); + if (common_name_asn1 == NULL) + { + return CMSError(); + } + + strSubjectCN.clear(); + strSubjectCN.append((const char *)common_name_asn1->data, common_name_asn1->length); + return (!strSubjectCN.empty()); +} + +bool GetCertSubjectCN(const string &strCertData, string &strSubjectCN) +{ + if (strCertData.empty()) + { + return false; + } + + BIO *bcert = BIO_new_mem_buf(strCertData.c_str(), strCertData.size()); + if (!bcert) + { + return CMSError(); + } + + X509 *cert = PEM_read_bio_X509(bcert, NULL, 0, NULL); + if (!cert) + { + return CMSError(); + } + + return GetCertSubjectCN(cert, strSubjectCN); +} + +void ParseCertSubject(const string &strSubject, JValue &jvSubject) +{ + vector arrNodes; + StringSplit(strSubject, "/", arrNodes); + for (size_t i = 0; i < arrNodes.size(); i++) + { + vector arrLines; + StringSplit(arrNodes[i], "=", arrLines); + if (2 == arrLines.size()) + { + jvSubject[arrLines[0]] = arrLines[1]; + } + } +} + +#if OPENSSL_VERSION_NUMBER < 0x10100000L +string ASN1_TIMEtoString(ASN1_TIME *time) +{ +#else +string ASN1_TIMEtoString(const ASN1_TIME *time) +{ +#endif + BIO *out = BIO_new(BIO_s_mem()); + if (!out) + { + CMSError(); + return ""; + } + + ASN1_TIME_print(out, time); + BUF_MEM *bptr = NULL; + BIO_get_mem_ptr(out, &bptr); + if (!bptr) + { + CMSError(); + return ""; + } + string strTime; + strTime.append(bptr->data, bptr->length); + return strTime; +} + +bool GetCertInfo(X509 *cert, JValue &jvCertInfo) +{ + if (!cert) + { + return CMSError(); + } + + jvCertInfo["Version"] = (int)X509_get_version(cert); + + ASN1_INTEGER *asn1_i = X509_get_serialNumber(cert); + if (asn1_i) + { + BIGNUM *bignum = ASN1_INTEGER_to_BN(asn1_i, NULL); + if (bignum) + { + jvCertInfo["SerialNumber"] = BN_bn2hex(bignum); + } + } + + jvCertInfo["SignatureAlgorithm"] = OBJ_nid2ln(X509_get_signature_nid(cert)); + + EVP_PKEY *pubkey = X509_get_pubkey(cert); + int type = EVP_PKEY_id(pubkey); + jvCertInfo["PublicKey"]["Algorithm"] = OBJ_nid2ln(type); + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + jvCertInfo["Validity"]["NotBefore"] = ASN1_TIMEtoString(X509_get_notBefore(cert)); + jvCertInfo["Validity"]["NotAfter"] = ASN1_TIMEtoString(X509_get_notAfter(cert)); +#else + jvCertInfo["Validity"]["NotBefore"] = ASN1_TIMEtoString(X509_get0_notBefore(cert)); + jvCertInfo["Validity"]["NotAfter"] = ASN1_TIMEtoString(X509_get0_notAfter(cert)); +#endif + + string strIssuer = X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0); + string strSubject = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0); + + ParseCertSubject(strIssuer, jvCertInfo["Issuer"]); + ParseCertSubject(strSubject, jvCertInfo["Subject"]); + + return (!strIssuer.empty() && !strSubject.empty()); +} + +bool GetCMSInfo(uint8_t *pCMSData, uint32_t uCMSLength, JValue &jvOutput) +{ + BIO *in = BIO_new(BIO_s_mem()); + OPENSSL_assert((size_t)BIO_write(in, pCMSData, uCMSLength) == uCMSLength); + CMS_ContentInfo *cms = d2i_CMS_bio(in, NULL); + if (!cms) + { + return CMSError(); + } + + int detached = CMS_is_detached(cms); + jvOutput["detached"] = detached; + + const ASN1_OBJECT *obj = CMS_get0_type(cms); + const char *sn = OBJ_nid2ln(OBJ_obj2nid(obj)); + jvOutput["contentType"] = sn; + + ASN1_OCTET_STRING **pos = CMS_get0_content(cms); + if (pos) + { + if ((*pos)) + { + ZBase64 b64; + jvOutput["content"] = b64.Encode((const char *)(*pos)->data, (*pos)->length); + } + } + + STACK_OF(X509) *certs = CMS_get1_certs(cms); + for (int i = 0; i < sk_X509_num(certs); i++) + { + JValue jvCertInfo; + if (GetCertInfo(sk_X509_value(certs, i), jvCertInfo)) + { + jvOutput["certs"].push_back(jvCertInfo); + } + } + + STACK_OF(CMS_SignerInfo) *sis = CMS_get0_SignerInfos(cms); + for (int i = 0; i < sk_CMS_SignerInfo_num(sis); i++) + { + CMS_SignerInfo *si = sk_CMS_SignerInfo_value(sis, i); + //int CMS_SignerInfo_get0_signer_id(CMS_SignerInfo *si, ASN1_OCTET_STRING **keyid, X509_NAME **issuer, ASN1_INTEGER **sno); + + int nSignedAttsCount = CMS_signed_get_attr_count(si); + for (int j = 0; j < nSignedAttsCount; j++) + { + X509_ATTRIBUTE *attr = CMS_signed_get_attr(si, j); + if (!attr) + { + continue; + } + int nCount = X509_ATTRIBUTE_count(attr); + if (nCount <= 0) + { + continue; + } + + ASN1_OBJECT *obj = X509_ATTRIBUTE_get0_object(attr); + if (!obj) + { + continue; + } + + char txtobj[128] = {0}; + OBJ_obj2txt(txtobj, 128, obj, 1); + + if (0 == strcmp("1.2.840.113549.1.9.3", txtobj)) + { //V_ASN1_OBJECT + ASN1_TYPE *av = X509_ATTRIBUTE_get0_type(attr, 0); + if (NULL != av) + { + jvOutput["attrs"]["ContentType"]["obj"] = txtobj; + jvOutput["attrs"]["ContentType"]["data"] = OBJ_nid2ln(OBJ_obj2nid(av->value.object)); + } + } + else if (0 == strcmp("1.2.840.113549.1.9.4", txtobj)) + { //V_ASN1_OCTET_STRING + ASN1_TYPE *av = X509_ATTRIBUTE_get0_type(attr, 0); + if (NULL != av) + { + string strSHASum; + char buf[16] = {0}; + for (int m = 0; m < av->value.octet_string->length; m++) + { + sprintf(buf, "%02x", (uint8_t)av->value.octet_string->data[m]); + strSHASum += buf; + } + jvOutput["attrs"]["MessageDigest"]["obj"] = txtobj; + jvOutput["attrs"]["MessageDigest"]["data"] = strSHASum; + } + } + else if (0 == strcmp("1.2.840.113549.1.9.5", txtobj)) + { //V_ASN1_UTCTIME + ASN1_TYPE *av = X509_ATTRIBUTE_get0_type(attr, 0); + if (NULL != av) + { + BIO *mem = BIO_new(BIO_s_mem()); + ASN1_UTCTIME_print(mem, av->value.utctime); + BUF_MEM *bptr = NULL; + BIO_get_mem_ptr(mem, &bptr); + BIO_set_close(mem, BIO_NOCLOSE); + string strTime; + strTime.append(bptr->data, bptr->length); + BIO_free_all(mem); + + jvOutput["attrs"]["SigningTime"]["obj"] = txtobj; + jvOutput["attrs"]["SigningTime"]["data"] = strTime; + } + } + else if (0 == strcmp("1.2.840.113635.100.9.2", txtobj)) + { //V_ASN1_SEQUENCE + jvOutput["attrs"]["CDHashes2"]["obj"] = txtobj; + for (int m = 0; m < nCount; m++) + { + ASN1_TYPE *av = X509_ATTRIBUTE_get0_type(attr, m); + if (NULL != av) + { + ASN1_STRING *s = av->value.sequence; + + BIO *mem = BIO_new(BIO_s_mem()); + + ASN1_parse_dump(mem, s->data, s->length, 2, 0); + BUF_MEM *bptr = NULL; + BIO_get_mem_ptr(mem, &bptr); + BIO_set_close(mem, BIO_NOCLOSE); + string strData; + strData.append(bptr->data, bptr->length); + BIO_free_all(mem); + + string strSHASum; + size_t pos1 = strData.find("[HEX DUMP]:"); + if (string::npos != pos1) + { + size_t pos2 = strData.find("\n", pos1); + if (string::npos != pos2) + { + strSHASum = strData.substr(pos1 + 11, pos2 - pos1 - 11); + } + } + transform(strSHASum.begin(), strSHASum.end(), strSHASum.begin(), ::tolower); + jvOutput["attrs"]["CDHashes2"]["data"].push_back(strSHASum); + } + } + } + else if (0 == strcmp("1.2.840.113635.100.9.1", txtobj)) + { //V_ASN1_OCTET_STRING + ASN1_TYPE *av = X509_ATTRIBUTE_get0_type(attr, 0); + if (NULL != av) + { + string strPList; + strPList.append((const char *)av->value.octet_string->data, av->value.octet_string->length); + jvOutput["attrs"]["CDHashes"]["obj"] = txtobj; + jvOutput["attrs"]["CDHashes"]["data"] = strPList; + } + } + else + { + ASN1_TYPE *av = X509_ATTRIBUTE_get0_type(attr, 0); + if (NULL != av) + { + JValue jvAttr; + jvAttr["obj"] = txtobj; + jvAttr["name"] = OBJ_nid2ln(OBJ_obj2nid(obj)); + jvAttr["type"] = av->type; + jvAttr["count"] = nCount; + jvOutput["attrs"]["unknown"].push_back(jvAttr); + } + } + } + } + + return true; +} + +ZSignAsset::ZSignAsset() +{ + m_evpPKey = NULL; + m_x509Cert = NULL; +} + +bool ZSignAsset::Init(const string &strSignerCertFile, const string &strSignerPKeyFile, const string &strProvisionFile, const string &strEntitlementsFile, const string &strPassword) +{ + ReadFile(strProvisionFile.c_str(), m_strProvisionData); + ReadFile(strEntitlementsFile.c_str(), m_strEntitlementsData); + if (m_strProvisionData.empty()) + { + ZLog::Error(">>> Can't Find Provision File!\n"); + return false; + } + + JValue jvProv; + string strProvContent; + if (GetCMSContent(m_strProvisionData, strProvContent)) + { + if (jvProv.readPList(strProvContent)) + { + m_strTeamId = jvProv["TeamIdentifier"][0].asCString(); + if (m_strEntitlementsData.empty()) + { + jvProv["Entitlements"].writePList(m_strEntitlementsData); + } + } + } + + if (m_strTeamId.empty()) + { + ZLog::Error(">>> Can't Find TeamId!\n"); + return false; + } + + X509 *x509Cert = NULL; + EVP_PKEY *evpPKey = NULL; + BIO *bioPKey = BIO_new_file(strSignerPKeyFile.c_str(), "r"); + if (NULL != bioPKey) + { + evpPKey = PEM_read_bio_PrivateKey(bioPKey, NULL, NULL, (void *)strPassword.c_str()); + if (NULL == evpPKey) + { + BIO_reset(bioPKey); + evpPKey = d2i_PrivateKey_bio(bioPKey, NULL); + if (NULL == evpPKey) + { + BIO_reset(bioPKey); + OSSL_PROVIDER_load(NULL, "legacy"); + PKCS12 *p12 = d2i_PKCS12_bio(bioPKey, NULL); + if (NULL != p12) + { + if (0 == PKCS12_parse(p12, strPassword.c_str(), &evpPKey, &x509Cert, NULL)) + { + CMSError(); + } + PKCS12_free(p12); + } + } + } + BIO_free(bioPKey); + } + + if (NULL == evpPKey) + { + ZLog::Error(">>> Can't Load P12 or PrivateKey File! Please Input The Correct File And Password!\n"); + return false; + } + + if (NULL == x509Cert && !strSignerCertFile.empty()) + { + BIO *bioCert = BIO_new_file(strSignerCertFile.c_str(), "r"); + if (NULL != bioCert) + { + x509Cert = PEM_read_bio_X509(bioCert, NULL, 0, NULL); + if (NULL == x509Cert) + { + BIO_reset(bioCert); + x509Cert = d2i_X509_bio(bioCert, NULL); + } + BIO_free(bioCert); + } + } + + if (NULL != x509Cert) + { + if (!X509_check_private_key(x509Cert, evpPKey)) + { + X509_free(x509Cert); + x509Cert = NULL; + } + } + + if (NULL == x509Cert) + { + for (size_t i = 0; i < jvProv["DeveloperCertificates"].size(); i++) + { + string strCertData = jvProv["DeveloperCertificates"][i].asData(); + BIO *bioCert = BIO_new_mem_buf(strCertData.c_str(), (int)strCertData.size()); + if (NULL != bioCert) + { + x509Cert = d2i_X509_bio(bioCert, NULL); + if (NULL != x509Cert) + { + if (X509_check_private_key(x509Cert, evpPKey)) + { + break; + } + X509_free(x509Cert); + x509Cert = NULL; + } + BIO_free(bioCert); + } + } + } + + if (NULL == x509Cert) + { + ZLog::Error(">>> Can't Find Paired Certificate And PrivateKey!\n"); + return false; + } + + if (!GetCertSubjectCN(x509Cert, m_strSubjectCN)) + { + ZLog::Error(">>> Can't Find Paired Certificate Subject Common Name!\n"); + return false; + } + + m_evpPKey = evpPKey; + m_x509Cert = x509Cert; + return true; +} + +bool ZSignAsset::GenerateCMS(const string &strCDHashData, const string &strCDHashesPlist, const string &strCodeDirectorySlotSHA1, const string &strAltnateCodeDirectorySlot256, string &strCMSOutput) +{ + return ::_GenerateCMS((X509 *)m_x509Cert, (EVP_PKEY *)m_evpPKey, strCDHashData, strCDHashesPlist, strCodeDirectorySlotSHA1, strAltnateCodeDirectorySlot256, strCMSOutput); +} + + std::string binary_to_hex(const char* data, int len) { + std::ostringstream oss; + for (int i = 0; i < len; ++i) { + uint8_t byte = data[i]; + oss << std::hex << std::setw(2) << std::setfill('0') << static_cast(byte); + } + return oss.str(); + } + +bool ZSignAsset::InitSimple(const void* strSignerPKeyData, int strSignerPKeyDataSize, const void* strProvisionData, int strProvisionDataSize, const string &strPassword){ + + JValue jvProv; + string strProvContent; + m_strEntitlementsData = ""; + if (GetCMSContent2(strProvisionData, strProvisionDataSize, strProvContent)) + { + if (jvProv.readPList(strProvContent)) + { + m_strTeamId = jvProv["TeamIdentifier"][0].asCString(); + if (m_strEntitlementsData.empty()) + { + jvProv["Entitlements"].writePList(m_strEntitlementsData); + } + } + } + + if (m_strTeamId.empty()) + { + ZLog::Error(">>> Can't Find TeamId!\n"); + return false; + } + + X509 *x509Cert = NULL; + EVP_PKEY *evpPKey = NULL; + + // BIO *bioPKey = BIO_new_file(strSignerPKeyFile.c_str(), "r"); + char* digest = new char[16]; + MD5((unsigned char*) strSignerPKeyData, strSignerPKeyDataSize, (unsigned char*)digest); + ZLog::Error(binary_to_hex(digest, 16).data()); + delete[] digest; + + + BIO *bioPKey = BIO_new_mem_buf(strSignerPKeyData, strSignerPKeyDataSize); + if (NULL != bioPKey) + { + evpPKey = PEM_read_bio_PrivateKey(bioPKey, NULL, NULL, (void *)strPassword.c_str()); + if (NULL == evpPKey) + { + BIO_reset(bioPKey); + evpPKey = d2i_PrivateKey_bio(bioPKey, NULL); + if (NULL == evpPKey) + { + BIO_reset(bioPKey); + OSSL_PROVIDER_load(NULL, "legacy"); + PKCS12 *p12 = d2i_PKCS12_bio(bioPKey, NULL); + if (NULL != p12) + { + if (0 == PKCS12_parse(p12, strPassword.c_str(), &evpPKey, &x509Cert, NULL)) + { + CMSError(); + } + PKCS12_free(p12); + } + } + } + BIO_free(bioPKey); + } + + if (NULL == evpPKey) + { + ZLog::Error(">>> Can't Load P12 or PrivateKey File! Please Input The Correct File And Password!\n"); + return false; + } + + if (NULL != x509Cert) + { + if (!X509_check_private_key(x509Cert, evpPKey)) + { + X509_free(x509Cert); + x509Cert = NULL; + } + } + + if (NULL == x509Cert) + { + for (size_t i = 0; i < jvProv["DeveloperCertificates"].size(); i++) + { + string strCertData = jvProv["DeveloperCertificates"][i].asData(); + BIO *bioCert = BIO_new_mem_buf(strCertData.c_str(), (int)strCertData.size()); + if (NULL != bioCert) + { + x509Cert = d2i_X509_bio(bioCert, NULL); + if (NULL != x509Cert) + { + if (X509_check_private_key(x509Cert, evpPKey)) + { + break; + } + X509_free(x509Cert); + x509Cert = NULL; + } + BIO_free(bioCert); + } + } + } + + if (NULL == x509Cert) + { + ZLog::Error(">>> Can't Find Paired Certificate And PrivateKey!\n"); + return false; + } + + if (!GetCertSubjectCN(x509Cert, m_strSubjectCN)) + { + ZLog::Error(">>> Can't Find Paired Certificate Subject Common Name!\n"); + return false; + } + + m_evpPKey = evpPKey; + m_x509Cert = x509Cert; + return true; +} diff --git a/ZSign/openssl.h b/ZSign/openssl.h new file mode 100644 index 0000000..6e4dfec --- /dev/null +++ b/ZSign/openssl.h @@ -0,0 +1,28 @@ +#pragma once +#include "common/json.h" + +bool GetCertSubjectCN(const string &strCertData, string &strSubjectCN); +bool GetCMSInfo(uint8_t *pCMSData, uint32_t uCMSLength, JValue &jvOutput); +bool GetCMSContent(const string &strCMSDataInput, string &strContentOutput); +bool GenerateCMS(const string &strSignerCertData, const string &strSignerPKeyData, const string &strCDHashData, const string &strCDHashPlist, string &strCMSOutput); + +class ZSignAsset +{ +public: + ZSignAsset(); + +public: + bool GenerateCMS(const string &strCDHashData, const string &strCDHashesPlist, const string &strCodeDirectorySlotSHA1, const string &strAltnateCodeDirectorySlot256, string &strCMSOutput); + bool Init(const string &strSignerCertFile, const string &strSignerPKeyFile, const string &strProvisionFile, const string &strEntitlementsFile, const string &strPassword); + bool InitSimple(const void* strSignerPKeyData, int strSignerPKeyDataSize, const void* strProvisionData, int strProvisionDataSize, const string &strPassword); + +public: + string m_strTeamId; + string m_strSubjectCN; + string m_strProvisionData; + string m_strEntitlementsData; + +private: + void *m_evpPKey; + void *m_x509Cert; +}; diff --git a/ZSign/signing.cpp b/ZSign/signing.cpp new file mode 100644 index 0000000..64d5dd7 --- /dev/null +++ b/ZSign/signing.cpp @@ -0,0 +1,875 @@ +#include "common/common.h" +#include "common/json.h" +#include "common/mach-o.h" +#include "openssl.h" + +static void _DERLength(string &strBlob, uint64_t uLength) +{ + if (uLength < 128) + { + strBlob.append(1, (char)uLength); + } + else + { + uint32_t sLength = (64 - __builtin_clzll(uLength) + 7) / 8; + strBlob.append(1, (char)(0x80 | sLength)); + sLength *= 8; + do + { + strBlob.append(1, (char)(uLength >> (sLength -= 8))); + } while (sLength != 0); + } +} + +static string _DER(const JValue &data) +{ + string strOutput; + if (data.isBool()) + { + strOutput.append(1, 0x01); + strOutput.append(1, 1); + strOutput.append(1, data.asBool() ? 1 : 0); + } + else if (data.isInt()) + { + uint64_t uVal = data.asInt64(); + strOutput.append(1, 0x02); + _DERLength(strOutput, uVal); + + uint32_t sLength = (64 - __builtin_clzll(uVal) + 7) / 8; + sLength *= 8; + do + { + strOutput.append(1, (char)(uVal >> (sLength -= 8))); + } while (sLength != 0); + } + else if (data.isString()) + { + string strVal = data.asCString(); + strOutput.append(1, 0x0c); + _DERLength(strOutput, strVal.size()); + strOutput += strVal; + } + else if (data.isArray()) + { + string strArray; + size_t size = data.size(); + for (size_t i = 0; i < size; i++) + { + strArray += _DER(data[i]); + } + strOutput.append(1, 0x30); + _DERLength(strOutput, strArray.size()); + strOutput += strArray; + } + else if (data.isObject()) + { + string strDict; + vector arrKeys; + data.keys(arrKeys); + for (size_t i = 0; i < arrKeys.size(); i++) + { + string &strKey = arrKeys[i]; + string strVal = _DER(data[strKey]); + + strDict.append(1, 0x30); + _DERLength(strDict, (2 + strKey.size() + strVal.size())); + + strDict.append(1, 0x0c); + _DERLength(strDict, strKey.size()); + strDict += strKey; + + strDict += strVal; + } + + strOutput.append(1, 0x31); + _DERLength(strOutput, strDict.size()); + strOutput += strDict; + } + else if (data.isFloat()) + { + assert(false); + } + else if (data.isDate()) + { + assert(false); + } + else if (data.isData()) + { + assert(false); + } + else + { + assert(false && "Unsupported Entitlements DER Type"); + } + + return strOutput; +} + +uint32_t SlotParseGeneralHeader(const char *szSlotName, uint8_t *pSlotBase, CS_BlobIndex *pbi) +{ + uint32_t uSlotLength = LE(*(((uint32_t *)pSlotBase) + 1)); + ZLog::PrintV("\n > %s: \n", szSlotName); + ZLog::PrintV("\ttype: \t\t0x%x\n", LE(pbi->type)); + ZLog::PrintV("\toffset: \t%u\n", LE(pbi->offset)); + ZLog::PrintV("\tmagic: \t\t0x%x\n", LE(*((uint32_t *)pSlotBase))); + ZLog::PrintV("\tlength: \t%u\n", uSlotLength); + return uSlotLength; +} + +void SlotParseGeneralTailer(uint8_t *pSlotBase, uint32_t uSlotLength) +{ + PrintDataSHASum("\tSHA-1: \t", E_SHASUM_TYPE_1, pSlotBase, uSlotLength); + PrintDataSHASum("\tSHA-256:\t", E_SHASUM_TYPE_256, pSlotBase, uSlotLength); +} + +bool SlotParseRequirements(uint8_t *pSlotBase, CS_BlobIndex *pbi) +{ + uint32_t uSlotLength = SlotParseGeneralHeader("CSSLOT_REQUIREMENTS", pSlotBase, pbi); + if (uSlotLength < 8) + { + return false; + } + + if (IsFileExists("/usr/bin/csreq")) + { + string strTempFile; + StringFormat(strTempFile, "/tmp/Requirements_%llu.blob", GetMicroSecond()); + WriteFile(strTempFile.c_str(), (const char *)pSlotBase, uSlotLength); + + string strCommand; + StringFormat(strCommand, "/usr/bin/csreq -r '%s' -t ", strTempFile.c_str()); + char result[1024] = {0}; + FILE *cmd = popen(strCommand.c_str(), "r"); + while (NULL != fgets(result, sizeof(result), cmd)) + { + printf("\treqtext: \t%s", result); + } + pclose(cmd); + RemoveFile(strTempFile.c_str()); + } + + SlotParseGeneralTailer(pSlotBase, uSlotLength); + + if (ZLog::IsDebug()) + { + WriteFile("./.zsign_debug/Requirements.slot", (const char *)pSlotBase, uSlotLength); + } + return true; +} + +bool SlotBuildRequirements(const string &strBundleID, const string &strSubjectCN, string &strOutput) +{ + strOutput.clear(); + if (strBundleID.empty() || strSubjectCN.empty()) + { //ldid + strOutput = "\xfa\xde\x0c\x01\x00\x00\x00\x0c\x00\x00\x00\x00"; + return true; + } + + string strPaddedBundleID = strBundleID; + strPaddedBundleID.append(((strBundleID.size() % 4) ? (4 - (strBundleID.size() % 4)) : 0), 0); + + string strPaddedSubjectID = strSubjectCN; + strPaddedSubjectID.append(((strSubjectCN.size() % 4) ? (4 - (strSubjectCN.size() % 4)) : 0), 0); + + uint8_t magic1[] = {0xfa, 0xde, 0x0c, 0x01}; + uint32_t uLength1 = 0; + uint8_t pack1[] = {0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x14}; + uint8_t magic2[] = {0xfa, 0xde, 0x0c, 0x00}; + uint32_t uLength2 = 0; + uint8_t pack2[] = {0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02}; + uint32_t uBundldIDLength = (uint32_t)strBundleID.size(); + //string strPaddedBundleID + uint8_t pack3[] = { + 0x00, + 0x00, + 0x00, + 0x06, + 0x00, + 0x00, + 0x00, + 0x0f, + 0x00, + 0x00, + 0x00, + 0x06, + 0x00, + 0x00, + 0x00, + 0x0b, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x0a, + 0x73, + 0x75, + 0x62, + 0x6a, + 0x65, + 0x63, + 0x74, + 0x2e, + 0x43, + 0x4e, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x01, + }; + uint32_t uSubjectCNLength = (uint32_t)strSubjectCN.size(); + //string strPaddedSubjectID + uint8_t pack4[] = { + 0x00, + 0x00, + 0x00, + 0x0e, + 0x00, + 0x00, + 0x00, + 0x01, + 0x00, + 0x00, + 0x00, + 0x0a, + 0x2a, + 0x86, + 0x48, + 0x86, + 0xf7, + 0x63, + 0x64, + 0x06, + 0x02, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + }; + + uLength2 += sizeof(magic2) + sizeof(uLength2) + sizeof(pack2); + uLength2 += sizeof(uBundldIDLength) + strPaddedBundleID.size(); + uLength2 += sizeof(pack3); + uLength2 += sizeof(uSubjectCNLength) + strPaddedSubjectID.size(); + uLength2 += sizeof(pack4); + + uLength1 += sizeof(magic1) + sizeof(uLength1) + sizeof(pack1); + uLength1 += uLength2; + + uLength1 = BE(uLength1); + uLength2 = BE(uLength2); + uBundldIDLength = BE(uBundldIDLength); + uSubjectCNLength = BE(uSubjectCNLength); + + strOutput.append((const char *)magic1, sizeof(magic1)); + strOutput.append((const char *)&uLength1, sizeof(uLength1)); + strOutput.append((const char *)pack1, sizeof(pack1)); + strOutput.append((const char *)magic2, sizeof(magic2)); + strOutput.append((const char *)&uLength2, sizeof(uLength2)); + strOutput.append((const char *)pack2, sizeof(pack2)); + strOutput.append((const char *)&uBundldIDLength, sizeof(uBundldIDLength)); + strOutput.append(strPaddedBundleID.data(), strPaddedBundleID.size()); + strOutput.append((const char *)pack3, sizeof(pack3)); + strOutput.append((const char *)&uSubjectCNLength, sizeof(uSubjectCNLength)); + strOutput.append(strPaddedSubjectID.data(), strPaddedSubjectID.size()); + strOutput.append((const char *)pack4, sizeof(pack4)); + + return true; +} + +bool SlotParseEntitlements(uint8_t *pSlotBase, CS_BlobIndex *pbi) +{ + uint32_t uSlotLength = SlotParseGeneralHeader("CSSLOT_ENTITLEMENTS", pSlotBase, pbi); + if (uSlotLength < 8) + { + return false; + } + + string strEntitlements = "\t\t\t"; + strEntitlements.append((const char *)pSlotBase + 8, uSlotLength - 8); + PWriter::StringReplace(strEntitlements, "\n", "\n\t\t\t"); + ZLog::PrintV("\tentitlements: \n%s\n", strEntitlements.c_str()); + + SlotParseGeneralTailer(pSlotBase, uSlotLength); + + if (ZLog::IsDebug()) + { + WriteFile("./.zsign_debug/Entitlements.slot", (const char *)pSlotBase, uSlotLength); + WriteFile("./.zsign_debug/Entitlements.plist", (const char *)pSlotBase + 8, uSlotLength - 8); + } + return true; +} + +bool SlotParseDerEntitlements(uint8_t *pSlotBase, CS_BlobIndex *pbi) +{ + uint32_t uSlotLength = SlotParseGeneralHeader("CSSLOT_DER_ENTITLEMENTS", pSlotBase, pbi); + if (uSlotLength < 8) + { + return false; + } + + SlotParseGeneralTailer(pSlotBase, uSlotLength); + + if (ZLog::IsDebug()) + { + WriteFile("./.zsign_debug/Entitlements.der.slot", (const char *)pSlotBase, uSlotLength); + } + return true; +} + +bool SlotBuildEntitlements(const string &strEntitlements, string &strOutput) +{ + strOutput.clear(); + if (strEntitlements.empty()) + { + return false; + } + + uint32_t uMagic = BE(CSMAGIC_EMBEDDED_ENTITLEMENTS); + uint32_t uLength = BE((uint32_t)strEntitlements.size() + 8); + + strOutput.append((const char *)&uMagic, sizeof(uMagic)); + strOutput.append((const char *)&uLength, sizeof(uLength)); + strOutput.append(strEntitlements.data(), strEntitlements.size()); + + return true; +} + +bool SlotBuildDerEntitlements(const string &strEntitlements, string &strOutput) +{ + strOutput.clear(); + if (strEntitlements.empty()) + { + return false; + } + + JValue jvInfo; + jvInfo.readPList(strEntitlements); + + string strRawEntitlementsData = _DER(jvInfo); + uint32_t uMagic = BE(CSMAGIC_EMBEDDED_DER_ENTITLEMENTS); + uint32_t uLength = BE((uint32_t)strRawEntitlementsData.size() + 8); + + strOutput.append((const char *)&uMagic, sizeof(uMagic)); + strOutput.append((const char *)&uLength, sizeof(uLength)); + strOutput.append(strRawEntitlementsData.data(), strRawEntitlementsData.size()); + + return true; +} + +bool SlotParseCodeDirectory(uint8_t *pSlotBase, CS_BlobIndex *pbi) +{ + uint32_t uSlotLength = SlotParseGeneralHeader("CSSLOT_CODEDIRECTORY", pSlotBase, pbi); + if (uSlotLength < 8) + { + return false; + } + + vector arrCodeSlots; + vector arrSpecialSlots; + CS_CodeDirectory cdHeader = *((CS_CodeDirectory *)pSlotBase); + uint8_t *pHashes = pSlotBase + LE(cdHeader.hashOffset); + for (uint32_t i = 0; i < LE(cdHeader.nCodeSlots); i++) + { + arrCodeSlots.push_back(pHashes + cdHeader.hashSize * i); + } + for (uint32_t i = 0; i < LE(cdHeader.nSpecialSlots); i++) + { + arrSpecialSlots.push_back(pHashes - cdHeader.hashSize * (i + 1)); + } + + ZLog::PrintV("\tversion: \t0x%x\n", LE(cdHeader.version)); + ZLog::PrintV("\tflags: \t\t%u\n", LE(cdHeader.flags)); + ZLog::PrintV("\thashOffset: \t%u\n", LE(cdHeader.hashOffset)); + ZLog::PrintV("\tidentOffset: \t%u\n", LE(cdHeader.identOffset)); + ZLog::PrintV("\tnSpecialSlots: \t%u\n", LE(cdHeader.nSpecialSlots)); + ZLog::PrintV("\tnCodeSlots: \t%u\n", LE(cdHeader.nCodeSlots)); + ZLog::PrintV("\tcodeLimit: \t%u\n", LE(cdHeader.codeLimit)); + ZLog::PrintV("\thashSize: \t%u\n", cdHeader.hashSize); + ZLog::PrintV("\thashType: \t%u\n", cdHeader.hashType); + ZLog::PrintV("\tspare1: \t%u\n", cdHeader.spare1); + ZLog::PrintV("\tpageSize: \t%u\n", cdHeader.pageSize); + ZLog::PrintV("\tspare2: \t%u\n", LE(cdHeader.spare2)); + + uint32_t uVersion = LE(cdHeader.version); + if (uVersion >= 0x20100) + { + ZLog::PrintV("\tscatterOffset: \t%u\n", LE(cdHeader.scatterOffset)); + } + if (uVersion >= 0x20200) + { + ZLog::PrintV("\tteamOffset: \t%u\n", LE(cdHeader.teamOffset)); + } + if (uVersion >= 0x20300) + { + ZLog::PrintV("\tspare3: \t%u\n", LE(cdHeader.spare3)); + ZLog::PrintV("\tcodeLimit64: \t%llu\n", LE(cdHeader.codeLimit64)); + } + if (uVersion >= 0x20400) + { + ZLog::PrintV("\texecSegBase: \t%llu\n", LE(cdHeader.execSegBase)); + ZLog::PrintV("\texecSegLimit: \t%llu\n", LE(cdHeader.execSegLimit)); + ZLog::PrintV("\texecSegFlags: \t%llu\n", LE(cdHeader.execSegFlags)); + } + + ZLog::PrintV("\tidentifier: \t%s\n", pSlotBase + LE(cdHeader.identOffset)); + if (uVersion >= 0x20200) + { + ZLog::PrintV("\tteamid: \t%s\n", pSlotBase + LE(cdHeader.teamOffset)); + } + + ZLog::PrintV("\tSpecialSlots:\n"); + for (int i = LE(cdHeader.nSpecialSlots) - 1; i >= 0; i--) + { + const char *suffix = "\t\n"; + switch (i) + { + case 0: + suffix = "\tInfo.plist\n"; + break; + case 1: + suffix = "\tRequirements Slot\n"; + break; + case 2: + suffix = "\tCodeResources\n"; + break; + case 3: + suffix = "\tApplication Specific\n"; + break; + case 4: + suffix = "\tEntitlements Slot\n"; + break; + case 6: + suffix = "\tEntitlements(DER) Slot\n"; + break; + } + PrintSHASum("\t\t\t", arrSpecialSlots[i], cdHeader.hashSize, suffix); + } + + if (ZLog::IsDebug()) + { + ZLog::Print("\tCodeSlots:\n"); + for (uint32_t i = 0; i < LE(cdHeader.nCodeSlots); i++) + { + PrintSHASum("\t\t\t", arrCodeSlots[i], cdHeader.hashSize); + } + } + else + { + ZLog::Print("\tCodeSlots: \tomitted. (use -d option for details)\n"); + } + + SlotParseGeneralTailer(pSlotBase, uSlotLength); + + if (ZLog::IsDebug()) + { + if (1 == cdHeader.hashType) + { + WriteFile("./.zsign_debug/CodeDirectory_SHA1.slot", (const char *)pSlotBase, uSlotLength); + } + else if (2 == cdHeader.hashType) + { + WriteFile("./.zsign_debug/CodeDirectory_SHA256.slot", (const char *)pSlotBase, uSlotLength); + } + } + + return true; +} + +bool SlotBuildCodeDirectory(bool bAlternate, + uint8_t *pCodeBase, + uint32_t uCodeLength, + uint8_t *pCodeSlotsData, + uint32_t uCodeSlotsDataLength, + uint64_t execSegLimit, + uint64_t execSegFlags, + const string &strBundleId, + const string &strTeamId, + const string &strInfoPlistSHA, + const string &strRequirementsSlotSHA, + const string &strCodeResourcesSHA, + const string &strEntitlementsSlotSHA, + const string &strDerEntitlementsSlotSHA, + bool isExecuteArch, + string &strOutput) +{ + strOutput.clear(); + if (NULL == pCodeBase || uCodeLength <= 0 || strBundleId.empty() || strTeamId.empty()) + { + return false; + } + + uint32_t uVersion = 0x20400; + + CS_CodeDirectory cdHeader; + memset(&cdHeader, 0, sizeof(cdHeader)); + cdHeader.magic = BE(CSMAGIC_CODEDIRECTORY); + cdHeader.length = 0; + cdHeader.version = BE(uVersion); + cdHeader.flags = 0; + cdHeader.hashOffset = 0; + cdHeader.identOffset = 0; + cdHeader.nSpecialSlots = 0; + cdHeader.nCodeSlots = 0; + cdHeader.codeLimit = BE(uCodeLength); + cdHeader.hashSize = bAlternate ? 32 : 20; + cdHeader.hashType = bAlternate ? 2 : 1; + cdHeader.spare1 = 0; + cdHeader.pageSize = 12; + cdHeader.spare2 = 0; + cdHeader.scatterOffset = 0; + cdHeader.teamOffset = 0; + cdHeader.execSegBase = 0; + cdHeader.execSegLimit = BE(execSegLimit); + cdHeader.execSegFlags = BE(execSegFlags); + + string strEmptySHA; + strEmptySHA.append(cdHeader.hashSize, 0); + vector arrSpecialSlots; + + if (isExecuteArch) + { + arrSpecialSlots.push_back(strDerEntitlementsSlotSHA.empty() ? strEmptySHA : strDerEntitlementsSlotSHA); + arrSpecialSlots.push_back(strEmptySHA); + } + arrSpecialSlots.push_back(strEntitlementsSlotSHA.empty() ? strEmptySHA : strEntitlementsSlotSHA); + arrSpecialSlots.push_back(strEmptySHA); + arrSpecialSlots.push_back(strCodeResourcesSHA.empty() ? strEmptySHA : strCodeResourcesSHA); + arrSpecialSlots.push_back(strRequirementsSlotSHA.empty() ? strEmptySHA : strRequirementsSlotSHA); + arrSpecialSlots.push_back(strInfoPlistSHA.empty() ? strEmptySHA : strInfoPlistSHA); + + uint32_t uPageSize = (uint32_t)pow(2, cdHeader.pageSize); + uint32_t uPages = uCodeLength / uPageSize; + uint32_t uRemain = uCodeLength % uPageSize; + uint32_t uCodeSlots = uPages + (uRemain > 0 ? 1 : 0); + + uint32_t uHeaderLength = 44; + if (uVersion >= 0x20100) + { + uHeaderLength += sizeof(cdHeader.scatterOffset); + } + if (uVersion >= 0x20200) + { + uHeaderLength += sizeof(cdHeader.teamOffset); + } + if (uVersion >= 0x20300) + { + uHeaderLength += sizeof(cdHeader.spare3); + uHeaderLength += sizeof(cdHeader.codeLimit64); + } + if (uVersion >= 0x20400) + { + uHeaderLength += sizeof(cdHeader.execSegBase); + uHeaderLength += sizeof(cdHeader.execSegLimit); + uHeaderLength += sizeof(cdHeader.execSegFlags); + } + + uint32_t uBundleIDLength = strBundleId.size() + 1; + uint32_t uTeamIDLength = strTeamId.size() + 1; + uint32_t uSpecialSlotsLength = arrSpecialSlots.size() * cdHeader.hashSize; + uint32_t uCodeSlotsLength = uCodeSlots * cdHeader.hashSize; + + uint32_t uSlotLength = uHeaderLength + uBundleIDLength + uSpecialSlotsLength + uCodeSlotsLength; + if (uVersion >= 0x20100) + { + //todo + } + if (uVersion >= 0x20200) + { + uSlotLength += uTeamIDLength; + } + + cdHeader.length = BE(uSlotLength); + cdHeader.identOffset = BE(uHeaderLength); + cdHeader.nSpecialSlots = BE((uint32_t)arrSpecialSlots.size()); + cdHeader.nCodeSlots = BE(uCodeSlots); + + uint32_t uHashOffset = uHeaderLength + uBundleIDLength + uSpecialSlotsLength; + if (uVersion >= 0x20100) + { + //todo + } + if (uVersion >= 0x20200) + { + uHashOffset += uTeamIDLength; + cdHeader.teamOffset = BE(uHeaderLength + uBundleIDLength); + } + cdHeader.hashOffset = BE(uHashOffset); + + strOutput.append((const char *)&cdHeader, uHeaderLength); + strOutput.append(strBundleId.data(), strBundleId.size() + 1); + if (uVersion >= 0x20100) + { + //todo + } + if (uVersion >= 0x20200) + { + strOutput.append(strTeamId.data(), strTeamId.size() + 1); + } + + for (uint32_t i = 0; i < LE(cdHeader.nSpecialSlots); i++) + { + strOutput.append(arrSpecialSlots[i].data(), arrSpecialSlots[i].size()); + } + + if (NULL != pCodeSlotsData && (uCodeSlotsDataLength == uCodeSlots * cdHeader.hashSize)) + { //use exists + strOutput.append((const char *)pCodeSlotsData, uCodeSlotsDataLength); + } + else + { + for (uint32_t i = 0; i < uPages; i++) + { + string strSHASum; + SHASum(cdHeader.hashType, pCodeBase + uPageSize * i, uPageSize, strSHASum); + strOutput.append(strSHASum.data(), strSHASum.size()); + } + if (uRemain > 0) + { + string strSHASum; + SHASum(cdHeader.hashType, pCodeBase + uPageSize * uPages, uRemain, strSHASum); + strOutput.append(strSHASum.data(), strSHASum.size()); + } + } + + return true; +} + +bool SlotParseCMSSignature(uint8_t *pSlotBase, CS_BlobIndex *pbi) +{ + uint32_t uSlotLength = SlotParseGeneralHeader("CSSLOT_SIGNATURESLOT", pSlotBase, pbi); + if (uSlotLength < 8) + { + return false; + } + + JValue jvInfo; + GetCMSInfo(pSlotBase + 8, uSlotLength - 8, jvInfo); + //ZLog::PrintV("%s\n", jvInfo.styleWrite().c_str()); + + ZLog::Print("\tCertificates: \n"); + for (size_t i = 0; i < jvInfo["certs"].size(); i++) + { + ZLog::PrintV("\t\t\t%s\t<=\t%s\n", jvInfo["certs"][i]["Subject"]["CN"].asCString(), jvInfo["certs"][i]["Issuer"]["CN"].asCString()); + } + + ZLog::Print("\tSignedAttrs: \n"); + if (jvInfo["attrs"].has("ContentType")) + { + ZLog::PrintV("\t ContentType: \t%s => %s\n", jvInfo["attrs"]["ContentType"]["obj"].asCString(), jvInfo["attrs"]["ContentType"]["data"].asCString()); + } + + if (jvInfo["attrs"].has("SigningTime")) + { + ZLog::PrintV("\t SigningTime: \t%s => %s\n", jvInfo["attrs"]["SigningTime"]["obj"].asCString(), jvInfo["attrs"]["SigningTime"]["data"].asCString()); + } + + if (jvInfo["attrs"].has("MessageDigest")) + { + ZLog::PrintV("\t MsgDigest: \t%s => %s\n", jvInfo["attrs"]["MessageDigest"]["obj"].asCString(), jvInfo["attrs"]["MessageDigest"]["data"].asCString()); + } + + if (jvInfo["attrs"].has("CDHashes")) + { + string strData = jvInfo["attrs"]["CDHashes"]["data"].asCString(); + StringReplace(strData, "\n", "\n\t\t\t\t"); + ZLog::PrintV("\t CDHashes: \t%s => \n\t\t\t\t%s\n", jvInfo["attrs"]["CDHashes"]["obj"].asCString(), strData.c_str()); + } + + if (jvInfo["attrs"].has("CDHashes2")) + { + ZLog::PrintV("\t CDHashes2: \t%s => \n", jvInfo["attrs"]["CDHashes2"]["obj"].asCString()); + for (size_t i = 0; i < jvInfo["attrs"]["CDHashes2"]["data"].size(); i++) + { + ZLog::PrintV("\t\t\t\t%s\n", jvInfo["attrs"]["CDHashes2"]["data"][i].asCString()); + } + } + + for (size_t i = 0; i < jvInfo["attrs"]["unknown"].size(); i++) + { + JValue &jvAttr = jvInfo["attrs"]["unknown"][i]; + ZLog::PrintV("\t UnknownAttr: \t%s => %s, type: %d, count: %d\n", jvAttr["obj"].asCString(), jvAttr["name"].asCString(), jvAttr["type"].asInt(), jvAttr["count"].asInt()); + } + ZLog::Print("\n"); + + SlotParseGeneralTailer(pSlotBase, uSlotLength); + + if (ZLog::IsDebug()) + { + WriteFile("./.zsign_debug/CMSSignature.slot", (const char *)pSlotBase, uSlotLength); + WriteFile("./.zsign_debug/CMSSignature.der", (const char *)pSlotBase + 8, uSlotLength - 8); + } + return true; +} + +bool SlotBuildCMSSignature(ZSignAsset *pSignAsset, + const string &strCodeDirectorySlot, + const string &strAltnateCodeDirectorySlot, + string &strOutput) +{ + strOutput.clear(); + + JValue jvHashes; + string strCDHashesPlist; + string strCodeDirectorySlotSHA1; + string strAltnateCodeDirectorySlot256; + SHASum(E_SHASUM_TYPE_1, strCodeDirectorySlot, strCodeDirectorySlotSHA1); + SHASum(E_SHASUM_TYPE_256, strAltnateCodeDirectorySlot, strAltnateCodeDirectorySlot256); + + size_t cdHashSize = strCodeDirectorySlotSHA1.size(); + jvHashes["cdhashes"][0].assignData(strCodeDirectorySlotSHA1.data(), cdHashSize); + jvHashes["cdhashes"][1].assignData(strAltnateCodeDirectorySlot256.data(), cdHashSize); + jvHashes.writePList(strCDHashesPlist); + + string strCMSData; + if (!pSignAsset->GenerateCMS(strCodeDirectorySlot, strCDHashesPlist, strCodeDirectorySlotSHA1, strAltnateCodeDirectorySlot256, strCMSData)) + { + return false; + } + + uint32_t uMagic = BE(CSMAGIC_BLOBWRAPPER); + uint32_t uLength = BE((uint32_t)strCMSData.size() + 8); + + strOutput.append((const char *)&uMagic, sizeof(uMagic)); + strOutput.append((const char *)&uLength, sizeof(uLength)); + strOutput.append(strCMSData.data(), strCMSData.size()); + return true; +} + +uint32_t GetCodeSignatureLength(uint8_t *pCSBase) +{ + CS_SuperBlob *psb = (CS_SuperBlob *)pCSBase; + if (NULL != psb && CSMAGIC_EMBEDDED_SIGNATURE == LE(psb->magic)) + { + return LE(psb->length); + } + return 0; +} + +bool ParseCodeSignature(uint8_t *pCSBase) +{ + CS_SuperBlob *psb = (CS_SuperBlob *)pCSBase; + if (NULL == psb || CSMAGIC_EMBEDDED_SIGNATURE != LE(psb->magic)) + { + return false; + } + + ZLog::PrintV("\n>>> CodeSignature Segment: \n"); + ZLog::PrintV("\tmagic: \t\t0x%x\n", LE(psb->magic)); + ZLog::PrintV("\tlength: \t%d\n", LE(psb->length)); + ZLog::PrintV("\tslots: \t\t%d\n", LE(psb->count)); + + CS_BlobIndex *pbi = (CS_BlobIndex *)(pCSBase + sizeof(CS_SuperBlob)); + for (uint32_t i = 0; i < LE(psb->count); i++, pbi++) + { + uint8_t *pSlotBase = pCSBase + LE(pbi->offset); + switch (LE(pbi->type)) + { + case CSSLOT_CODEDIRECTORY: + SlotParseCodeDirectory(pSlotBase, pbi); + break; + case CSSLOT_REQUIREMENTS: + SlotParseRequirements(pSlotBase, pbi); + break; + case CSSLOT_ENTITLEMENTS: + SlotParseEntitlements(pSlotBase, pbi); + break; + case CSSLOT_DER_ENTITLEMENTS: + SlotParseDerEntitlements(pSlotBase, pbi); + break; + case CSSLOT_ALTERNATE_CODEDIRECTORIES: + SlotParseCodeDirectory(pSlotBase, pbi); + break; + case CSSLOT_SIGNATURESLOT: + SlotParseCMSSignature(pSlotBase, pbi); + break; + case CSSLOT_IDENTIFICATIONSLOT: + SlotParseGeneralHeader("CSSLOT_IDENTIFICATIONSLOT", pSlotBase, pbi); + break; + case CSSLOT_TICKETSLOT: + SlotParseGeneralHeader("CSSLOT_TICKETSLOT", pSlotBase, pbi); + break; + default: + SlotParseGeneralTailer(pSlotBase, SlotParseGeneralHeader("CSSLOT_UNKNOWN", pSlotBase, pbi)); + break; + } + } + + if (ZLog::IsDebug()) + { + WriteFile("./.zsign_debug/CodeSignature.blob", (const char *)pCSBase, LE(psb->length)); + } + return true; +} + +bool SlotGetCodeSlotsData(uint8_t *pSlotBase, uint8_t *&pCodeSlots, uint32_t &uCodeSlotsLength) +{ + uint32_t uSlotLength = LE(*(((uint32_t *)pSlotBase) + 1)); + if (uSlotLength < 8) + { + return false; + } + CS_CodeDirectory cdHeader = *((CS_CodeDirectory *)pSlotBase); + pCodeSlots = pSlotBase + LE(cdHeader.hashOffset); + uCodeSlotsLength = LE(cdHeader.nCodeSlots) * cdHeader.hashSize; + return true; +} + +bool GetCodeSignatureExistsCodeSlotsData(uint8_t *pCSBase, + uint8_t *&pCodeSlots1Data, + uint32_t &uCodeSlots1DataLength, + uint8_t *&pCodeSlots256Data, + uint32_t &uCodeSlots256DataLength) +{ + pCodeSlots1Data = NULL; + pCodeSlots256Data = NULL; + uCodeSlots1DataLength = 0; + uCodeSlots256DataLength = 0; + CS_SuperBlob *psb = (CS_SuperBlob *)pCSBase; + if (NULL == psb || CSMAGIC_EMBEDDED_SIGNATURE != LE(psb->magic)) + { + return false; + } + + CS_BlobIndex *pbi = (CS_BlobIndex *)(pCSBase + sizeof(CS_SuperBlob)); + for (uint32_t i = 0; i < LE(psb->count); i++, pbi++) + { + uint8_t *pSlotBase = pCSBase + LE(pbi->offset); + switch (LE(pbi->type)) + { + case CSSLOT_CODEDIRECTORY: + { + CS_CodeDirectory cdHeader = *((CS_CodeDirectory *)pSlotBase); + if (LE(cdHeader.length) > 8) + { + pCodeSlots1Data = pSlotBase + LE(cdHeader.hashOffset); + uCodeSlots1DataLength = LE(cdHeader.nCodeSlots) * cdHeader.hashSize; + } + } + break; + case CSSLOT_ALTERNATE_CODEDIRECTORIES: + { + CS_CodeDirectory cdHeader = *((CS_CodeDirectory *)pSlotBase); + if (LE(cdHeader.length) > 8) + { + pCodeSlots256Data = pSlotBase + LE(cdHeader.hashOffset); + uCodeSlots256DataLength = LE(cdHeader.nCodeSlots) * cdHeader.hashSize; + } + } + break; + default: + break; + } + } + + return ((NULL != pCodeSlots1Data) && (NULL != pCodeSlots256Data) && uCodeSlots1DataLength > 0 && uCodeSlots256DataLength > 0); +} diff --git a/ZSign/signing.h b/ZSign/signing.h new file mode 100644 index 0000000..df026fc --- /dev/null +++ b/ZSign/signing.h @@ -0,0 +1,34 @@ +#pragma once +#include "openssl.h" + +bool ParseCodeSignature(uint8_t *pCSBase); +bool SlotBuildEntitlements(const string &strEntitlements, string &strOutput); +bool SlotBuildDerEntitlements(const string &strEntitlements, string &strOutput); +bool SlotBuildRequirements(const string &strBundleID, const string &strSubjectCN, string &strOutput); +bool GetCodeSignatureCodeSlotsData(uint8_t *pCSBase, uint8_t *&pCodeSlots1, uint32_t &uCodeSlots1Length, uint8_t *&pCodeSlots256, uint32_t &uCodeSlots256Length); +bool SlotBuildCodeDirectory(bool bAlternate, + uint8_t *pCodeBase, + uint32_t uCodeLength, + uint8_t *pCodeSlotsData, + uint32_t uCodeSlotsDataLength, + uint64_t execSegLimit, + uint64_t execSegFlags, + const string &strBundleId, + const string &strTeamId, + const string &strInfoPlistSHA, + const string &strRequirementsSlotSHA, + const string &strCodeResourcesSHA, + const string &strEntitlementsSlotSHA, + const string &strDerEntitlementsSlotSHA, + bool isExecuteArch, + string &strOutput); +bool SlotBuildCMSSignature(ZSignAsset *pSignAsset, + const string &strCodeDirectorySlot, + const string &strAltnateCodeDirectorySlot, + string &strOutput); +bool GetCodeSignatureExistsCodeSlotsData(uint8_t *pCSBase, + uint8_t *&pCodeSlots1Data, + uint32_t &uCodeSlots1DataLength, + uint8_t *&pCodeSlots256Data, + uint32_t &uCodeSlots256DataLength); +uint32_t GetCodeSignatureLength(uint8_t *pCSBase); diff --git a/ZSign/zsign.hpp b/ZSign/zsign.hpp new file mode 100644 index 0000000..e3446c2 --- /dev/null +++ b/ZSign/zsign.hpp @@ -0,0 +1,44 @@ +// +// zsign.hpp +// feather +// +// Created by HAHALOSAH on 5/22/24. +// + +#ifndef zsign_hpp +#define zsign_hpp + +#include +#import + +#ifdef __cplusplus +extern "C" { +#endif + +bool InjectDyLib(NSString *filePath, + NSString *dylibPath, + bool weakInject, + bool bCreate); + +bool ChangeDylibPath(NSString *filePath, + NSString *oldPath, + NSString *newPath); + +bool ListDylibs(NSString *filePath, NSMutableArray *dylibPathsArray); +bool UninstallDylibs(NSString *filePath, NSArray *dylibPathsArray); + +void zsign(NSString *appPath, + NSString* execName, + NSData *prov, + NSData *key, + NSString *pass, + NSProgress* progress, + void(^completionHandler)(BOOL success, NSError *error) + ); + + +#ifdef __cplusplus +} +#endif + +#endif /* zsign_hpp */ diff --git a/ZSign/zsign.mm b/ZSign/zsign.mm new file mode 100644 index 0000000..62b055e --- /dev/null +++ b/ZSign/zsign.mm @@ -0,0 +1,232 @@ +#include "zsign.hpp" +#include "common/common.h" +#include "common/json.h" +#include "openssl.h" +#include "macho.h" +#include "bundle.h" +#include +#include +#include +#include + +NSString* getTmpDir() { + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + return [[[paths objectAtIndex:0] stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"tmp"]; +} + +extern "C" { + +bool InjectDyLib(NSString *filePath, NSString *dylibPath, bool weakInject, bool bCreate) { + ZTimer gtimer; + @autoreleasepool { + // Convert NSString to std::string + std::string filePathStr = [filePath UTF8String]; + std::string dylibPathStr = [dylibPath UTF8String]; + + ZMachO machO; + bool initSuccess = machO.Init(filePathStr.c_str()); + if (!initSuccess) { + gtimer.Print(">>> Failed to initialize ZMachO."); + return false; + } + + bool success = machO.InjectDyLib(weakInject, dylibPathStr.c_str(), bCreate); + + machO.Free(); + + if (success) { + gtimer.Print(">>> Dylib injected successfully!"); + return true; + } else { + gtimer.Print(">>> Failed to inject dylib."); + return false; + } + } +} + +bool ListDylibs(NSString *filePath, NSMutableArray *dylibPathsArray) { + ZTimer gtimer; + @autoreleasepool { + // Convert NSString to std::string + std::string filePathStr = [filePath UTF8String]; + + ZMachO machO; + bool initSuccess = machO.Init(filePathStr.c_str()); + if (!initSuccess) { + gtimer.Print(">>> Failed to initialize ZMachO."); + return false; + } + + std::vector dylibPaths = machO.ListDylibs(); + + if (!dylibPaths.empty()) { + gtimer.Print(">>> List of dylibs in the Mach-O file:"); + for (vector::iterator it = dylibPaths.begin(); it < dylibPaths.end(); ++it) { + std::string dylibPath = *it; + NSString *dylibPathStr = [NSString stringWithUTF8String:dylibPath.c_str()]; + [dylibPathsArray addObject:dylibPathStr]; + } + } else { + gtimer.Print(">>> No dylibs found in the Mach-O file."); + } + + machO.Free(); + + return true; + } +} + +bool UninstallDylibs(NSString *filePath, NSArray *dylibPathsArray) { + ZTimer gtimer; + @autoreleasepool { + std::string filePathStr = [filePath UTF8String]; + std::set dylibsToRemove; + + for (NSString *dylibPath in dylibPathsArray) { + dylibsToRemove.insert([dylibPath UTF8String]); + } + + ZMachO machO; + bool initSuccess = machO.Init(filePathStr.c_str()); + if (!initSuccess) { + gtimer.Print(">>> Failed to initialize ZMachO."); + return false; + } + + machO.RemoveDylib(dylibsToRemove); + + machO.Free(); + + gtimer.Print(">>> Dylibs uninstalled successfully!"); + return true; + } +} + + + +bool ChangeDylibPath(NSString *filePath, NSString *oldPath, NSString *newPath) { + ZTimer gtimer; + @autoreleasepool { + // Convert NSString to std::string + std::string filePathStr = [filePath UTF8String]; + std::string oldPathStr = [oldPath UTF8String]; + std::string newPathStr = [newPath UTF8String]; + + ZMachO machO; + bool initSuccess = machO.Init(filePathStr.c_str()); + if (!initSuccess) { + gtimer.Print(">>> Failed to initialize ZMachO."); + return false; + } + + bool success = machO.ChangeDylibPath(oldPathStr.c_str(), newPathStr.c_str()); + + machO.Free(); + + if (success) { + gtimer.Print(">>> Dylib path changed successfully!"); + return true; + } else { + gtimer.Print(">>> Failed to change dylib path."); + return false; + } + } +} + +NSError* makeErrorFromLog(const std::vector& vec) { + NSMutableString *result = [NSMutableString string]; + + for (size_t i = 0; i < vec.size(); ++i) { + // Convert each std::string to NSString + NSString *str = [NSString stringWithUTF8String:vec[i].c_str()]; + [result appendString:str]; + + // Append newline if it's not the last element + if (i != vec.size() - 1) { + [result appendString:@"\n"]; + } + } + + NSDictionary* userInfo = @{ + NSLocalizedDescriptionKey : result + }; + return [NSError errorWithDomain:@"Failed to Sign" code:-1 userInfo:userInfo]; +} + +ZSignAsset zSignAsset; + +void zsign(NSString *appPath, + NSString* execName, + NSData *prov, + NSData *key, + NSString *pass, + NSProgress* progress, + void(^completionHandler)(BOOL success, NSError *error) + ) +{ + ZTimer gtimer; + ZTimer timer; + timer.Reset(); + + bool bForce = false; + bool bWeakInject = false; + bool bDontGenerateEmbeddedMobileProvision = YES; + + string strPassword; + + string strDyLibFile; + string strOutputFile; + + string strEntitlementsFile; + + bForce = true; + const char* strPKeyFileData = (const char*)[key bytes]; + const char* strProvFileData = (const char*)[prov bytes]; + strPassword = [pass cStringUsingEncoding:NSUTF8StringEncoding]; + + + string strPath = [appPath cStringUsingEncoding:NSUTF8StringEncoding]; + string execNameStr = [execName cStringUsingEncoding:NSUTF8StringEncoding]; + + bool _ = ZLog::logs.empty(); + + __block ZSignAsset zSignAsset; + + if (!zSignAsset.InitSimple(strPKeyFileData, (int)[key length], strProvFileData, (int)[prov length], strPassword)) { + completionHandler(NO, makeErrorFromLog(ZLog::logs)); + bool _ = ZLog::logs.empty(); + return; + } + + bool bEnableCache = true; + string strFolder = strPath; + + __block ZAppBundle bundle; + bool success = bundle.ConfigureFolderSign(&zSignAsset, strFolder, execNameStr, "", "", "", strDyLibFile, bForce, bWeakInject, bEnableCache, bDontGenerateEmbeddedMobileProvision); + + if(!success) { + completionHandler(NO, makeErrorFromLog(ZLog::logs)); + bool _ = ZLog::logs.empty(); + return; + } + + int filesNeedToSign = bundle.GetSignCount(); + [progress setTotalUnitCount:filesNeedToSign]; + bundle.progressHandler = [&progress] { + [progress setCompletedUnitCount:progress.completedUnitCount + 1]; + }; + + + + + ZLog::PrintV(">>> Files Need to Sign: \t%d\n", filesNeedToSign); + bool bRet = bundle.StartSign(bEnableCache); + timer.PrintResult(bRet, ">>> Signed %s!", bRet ? "OK" : "Failed"); + gtimer.Print(">>> Done."); + completionHandler(YES, nil); + _ = ZLog::logs.empty(); + + return; +} + +} diff --git a/ZSign/zsigner.h b/ZSign/zsigner.h new file mode 100644 index 0000000..8c5ce4d --- /dev/null +++ b/ZSign/zsigner.h @@ -0,0 +1,11 @@ +// +// zsigner.h +// LiveContainer +// +// Created by s s on 2024/11/10. +// +#import + +@interface ZSigner : NSObject ++ (NSProgress*)signWithAppPath:(NSString *)appPath execName:(NSString *)execName prov:(NSData *)prov key:(NSData *)key pass:(NSString *)pass completionHandler:(void (^)(BOOL success, NSError *error))completionHandler; +@end diff --git a/ZSign/zsigner.m b/ZSign/zsigner.m new file mode 100644 index 0000000..ffc7def --- /dev/null +++ b/ZSign/zsigner.m @@ -0,0 +1,22 @@ +// +// zsigner.m +// LiveContainer +// +// Created by s s on 2024/11/10. +// + +#import "zsigner.h" +#import "zsign.hpp" + +NSProgress* currentZSignProgress; + +@implementation ZSigner ++ (NSProgress*)signWithAppPath:(NSString *)appPath execName:(NSString *)execName prov:(NSData *)prov key:(NSData *)key pass:(NSString *)pass completionHandler:(void (^)(BOOL success, NSError *error))completionHandler { + NSProgress* ans = [NSProgress progressWithTotalUnitCount:1000]; + NSLog(@"[LC] init sign!"); + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + zsign(appPath, execName, prov, key, pass, ans, completionHandler); + }); + return ans; +} +@end