ios 签名本质
签名的本质
是用于验证数据的合法性,确保被签名的数据来自特定的来源,并且未经篡改。它基于非对称加密,和哈希算法,研究签名之前需要对这两种算法有一定的了解。
为什么要签名?
数字签名其实跟我们手写的签名类似,
代表一个特定的主体(签名者)对特定内容(被签名数据)的署名和认可,
签名是对信息发送行为真实性的有效保障。
数字签名在很多领域都有应用,
iOS的代码签名正是其中最典型的一种,
我们可以先尝试分析一下iOS上代码签名的目的和好处。
A - 安全性
代码签名的首要任务是保证设备及系统的安全性,只有被苹果设备认可的证书签名的代码才能够被执行。
否则在安装或者运行时会因为无法通过内核的签名校验而失败。
iOS的系统中内置了来自苹果的CA证书,系统自身的代码都是被苹果”签名“过的, 而用户从AppStore下载的App也都已被苹果官方进行签名。签名机制可以有效地防止来自外部的攻击。
这里存在两种场景:
第一种是对系统本身的攻击,比如越狱,假如黑客发现了内核任意读写的漏洞,借此注入提权代码,但是这些代码会因为没有合法的签名而被系统拒绝运行,也就自然无法对系统造成实质性的破坏。
第二种是对设备或者用户的攻击,众所周知,提交到AppStore的应用代码都会经过苹果的审查,包含恶意代码的App是无法上架的。
此时,黑客可能会尝试先提交一个正常的App,通过各种技术手段躲避Apple的审查,上架后从网络上下载恶意代码并加载执行,但这种方式也会因为签名不合法而失败。
B - 垄断
代码签名还给苹果带来了一个巨大的好处:App分发的绝对控制权。
在iOS平台上(面向未越狱的用户)公开发行App的合法途径有且只有一种,就是上传到苹果官方的AppStore供用户下载。
苹果会对App进行严格的审查并签名,App的功能及支付渠道也因此可以受苹果的严格管制,这为苹果带来的经济效益不言而喻。
----以上两点参考别的博客----
C 签名的证书
image.png我们的Mac电脑中包含了一对公私钥 ,公钥M,私钥M,
向苹果申请证书,我们首先要生成csr文件,csr文件中包括了公钥M。
在钥匙串->证书助理->从证书颁发机构请求证书,
填写相关信息后会生成一个CertificateSigningRequest.certSigningRequest 证书,然后在开发者网站上上传这个文件,就可以生成这证书。
然后下载这个证书,双击安装到钥匙串。
这个证书中包含了公钥M,
还有用私钥A对公钥M签名文件进行RSA加密后的文件。
在开发这网站上选择bundleID生成相应的测试,上架描述文件,
当然,也可以在xcode中生成。然后项目中选择这个bundleID对应的描述文件进行运行。
描述文件中包含了苹果后端注册的设备列表,APPID,还有权限文件。
Xocde 在运行的过程中会使用电脑中的私钥(也可以是我们导出来的p12文件),
将描述文件和从开发者网站下载的证书一起打包进ipa文件中。
也是说ipa文件中包含了MachO文件,MachO文件的签名,
描述文件还有从苹果开发者网站上下载下来的证书。
一个ipa的包含结构是什么样的呢?
一个非砸壳的ipa里面可用有效文件也许就是图片资源了.
那么一个砸壳或者这个ipa没有加密他的内容可以怎么操作呢?
在编译iOS App时,Xcode在编译的打包的流程中会自动进行代码签名, 可以在编译日志界面找到一个Sign的步骤,内部是调用了codesign这个命令对app进行签名.
就像我们往常使用的自动打包上传脚本一样里面用的就是codesign
#使用方法 ./shell.sh
project_path=$(cd `dirname $0`; pwd)
if [ ! -d ./IPADir ];
then
mkdir -p IPADir;
fi
rm -rf /${project_path}/build
#工程绝对路径
ACCOUNT='开发者账号'
PASSWORD='专用密码'
#工程名 将XXX替换成自己的工程名
project_name='sheme'
#scheme名 将XXX替换成自己的sheme名
scheme_name='sheme'
#打包模式 Debug/Release
development_mode=Debug
#build文件夹路径
build_path=${project_path}/build
#plist文件所在路径
exportOptionsPlistPath=${project_path}/ExportOptions.plist
#导出.ipa文件所在路径
exportIpaPath=${project_path}/IPADir
echo "Place enter the number you want to export ? [ 1:app-store 2:ad-hoc] "
##
read number
while([[ $number != 1 ]] && [[ $number != 2 ]])
do
echo "Error! Should enter 1 or 2"
echo "Place enter the number you want to export ? [ 1:app-store 2:ad-hoc] "
read number
done
if [ $number == 1 ];then
development_mode=Release
exportOptionsPlistPath=${project_path}/exportAppstore.plist
else
development_mode=Debug
exportOptionsPlistPath=${project_path}/ExportOptions.plist
fi
echo '///-----------'
echo '/// 正在清理工程'
echo '///-----------'
xcodebuild \
clean -configuration ${development_mode} -quiet || exit
echo '///--------'
echo '/// 清理完成'
echo '///--------'
echo ''
echo '///-----------'
echo '/// 正在编译工程:'${development_mode}
echo '///-----------'
xcodebuild \
archive -workspace ${project_path}/${project_name}.xcworkspace \
-scheme ${scheme_name} \
-configuration ${development_mode} \
-archivePath ${build_path}/${project_name}.xcarchive -quiet || exit
echo '///--------'
echo '/// 编译完成'
echo '///--------'
echo ''
echo '///----------'
echo '/// 开始ipa打包'
echo '///----------'
xcodebuild -exportArchive -archivePath ${build_path}/${project_name}.xcarchive \
-configuration ${development_mode} \
-exportPath ${exportIpaPath} \
-exportOptionsPlist ${exportOptionsPlistPath} \
-quiet || exit
if [ -e $exportIpaPath/$scheme_name.ipa ]; then
echo '///----------'
echo '/// ipa包已导出'
echo '///----------'
#open $exportIpaPath
else
echo '///-------------'
echo '/// ipa包导出失败 '
echo '///-------------'
fi
echo '///------------'
echo '/// 打包ipa完成 '
echo '///-----------='
echo ''
echo '///-------------'
echo '/// 开始发布ipa包 '
echo '///-------------'
if [ $number == 1 ];then
#验证并上传到App Store
xcrun altool --validate-app -f ${exportIpaPath}/${scheme_name}.ipa -u ${ACCOUNT} -p ${PASSWORD} -t ios --output-format normal
xcrun altool --upload-app -f ${exportIpaPath}/${scheme_name}.ipa -u ${ACCOUNT} -p ${PASSWORD} -t ios --output-format normal
rm -rf /${project_path}/build
rm -rf /${project_path}/IPADir
echo '/// ipa包已成功上传App Store'
else
curl -F "file=@$exportIpaPath/$scheme_name.ipa" \
-F "uKey=0a5202ce3899e429bc549221f471f3ae90" \
-F "_api_key=5e4f5bf63400bcd3f5c2d72b0a74337f99" \
'https://www.pgyer.com/apiv2/app/upload'
echo '/// ipa包已成功上传蒲公英'
open https://www.pgyer.com/6Vrs
curl 'https://oapi.dingtalk.com/robot/send?access_token=19b93f88b2dc401e40791950cddd4cfbd79358f6941964f69cb83efb221aeb8b11' \
-H 'Content-Type: application/json' \
-d "
{\"msgtype\": \"text\",
\"text\": {
\"content\": \"测试版下载链接\n\'https://www.pgyer.com/6Vrs'\"
}
}"
fi
exit 0
CodeSign的有用的几个命令
检测APP是否完整
$ codesign --verify Example.app
直接使用证书对app签名(这里的证书到钥匙串直接复制证书名称即可)
$ codesign -s 'iPhone Developer: 151245000@qq.com (4KPK36MFV)' Example.app
重签名命令 和上面的区别就是要带上-f
$ codesign -f -s 'iPhone Developer: 151245000@qq.com (4KPK36MFV)' Example.app
打印 entitlements
$ codesign -d --entitlements - Example.app
相关的一个Mac 自带的打印签名进ipa里面的文件内容的命令
$ security cms -D -I example.mobileprovision
那么我们查看一下Ipa文件里面的签名文件的内容吧!
image.png双击打开并复制里面的内容,vi 然后创建文件粘贴刚才的内容.:wq保存.
image.png
接下来我们看一下这个签名文件里面到底是什么:
image.png
image.png
image.png
files和files2分别是旧版本和新版本的文件列表,而rules与rules2分别是与之对应的规则说明,
里面描述了计算hash时需要被排除的文件以及每个文件的权重。
files中保存的是每个文件的sha1值,
而files2中同时保存了sha1和sha256,
因为sha1在计算机硬件高度发达的今天,已经相对没有那么安全了,因此最新的签名算法中,引入了sha256。
注意,这里的hash值都是base64编码的明文.
通过对file1文件的查看对应的value就是文件对应的hash值,对于苹果的算法,
众多大神都是无法参破的.否则带来的影响那是不言而喻的.
当我们对ipa进行签名的时候,完成了就会生成这个codeResource.
image.png
当我们点击app 的应用图标的时候,苹果拿embedded.mobileprovision里面的信息和coderesources里面的信息进行比对,如果你改了一个内容但是并没有重新签名那么这个ipa的完整性就被破坏了,如果你的证书过期了那么app就会闪退.或者说你的app安装不上.出现什么完整性的问题.
当然embedded.mobileprovision也是可以导出成plist.
image.png
而codesign签名则是对所有文件包括资源文件进行一个遍历和hash值的存储.
App的安装是由/usr/libexec/installd完成的,
installd会通过libmis.dylib校验ProvisioningProfile、Entitlements
及签名的合法性,并递归地校验签名时每一个步骤生成的哈希值:
CDHash, Code Directory, _CodeSignature/CodeResources。
接下来我们详细看一下ZSign的文件
image.pngZSign 的.h文件
int zsign(int argc, char * argv[]);
ZSign 的.cpp文件签名命令
int zsign(int argc, char * argv[])
{
ZTimer gtimer;
bool bForce = true;
bool bInstall = false;
bool bWeakInject = false;
uint32_t uZipLevel = 5;
string strCertFile;
string strPKeyFile;
string strProvFile;
string strPassword;
string strBundleId;
string strBundleVersion;
string strDisplayName;
string strEntitlementsFile;
string strOutputFile;
string fromIpaPath;
for (int i = 0; i < argc; i += 2) {
char* option = argv[i];
if (strcmp(option, "-k") == 0) {
strPKeyFile = argv[i+1];
} else if (strcmp(option, "-c") == 0) {
strCertFile = argv[i+1];
} else if (strcmp(option, "-p") == 0) {
strPassword = argv[i+1];
} else if (strcmp(option, "-m") == 0) {
strProvFile = argv[i+1];
} else if (strcmp(option, "-o") == 0) {
strOutputFile = argv[i+1];
} else if (strcmp(option, "-v") == 0) {
strBundleVersion = argv[i+1];
} else if (strcmp(option, "-b") == 0) {
strBundleId = argv[i+1];
} else if (strcmp(option, "-n") == 0) {
strDisplayName = argv[i+1];
} else if (strcmp(option, "-z") == 0) {
uZipLevel = atoi(argv[i+1]);
} else if (strcmp(option, "-i") == 0) {
fromIpaPath = argv[i+1];
}
}
string strPath = fromIpaPath;
if (!IsFileExists(strPath.c_str()))
{
ZLog::ErrorV(">>> Invalid Path! %s\n", strPath.c_str());
return -1;
}
bool bZipFile = false;
if (!IsFolder(strPath.c_str()))
{
bZipFile = IsZipFile(strPath.c_str());
if (!bZipFile)
{ //macho file
ZMachO macho;
if (macho.Init(strPath.c_str()))
{
macho.Free();
}
return 0;
}
}
ZTimer timer;
ZSignAsset zSignAsset;
if (!zSignAsset.Init(strCertFile, strPKeyFile, strProvFile, strEntitlementsFile, strPassword))
{
return -2;
}
MyCPPClass *temp = new MyCPPClass();
temp->init();
//temp->getAppCachePath((char* )fromIpaPath.c_str())
char* appCachePath = (char* )fromIpaPath.c_str();
//unzip
bool bEnableCache = true;
string strFolder = GetCanonicalizePath(appCachePath);
if (bZipFile)
{ //ipa file
bForce = true;
bEnableCache = false;
ZLog::PrintV(">>> Unzip:\t%s (%s) -> %s ... \n", strPath.c_str(), GetFileSizeString(strPath.c_str()).c_str(), strFolder.c_str());
if (!temp->unzip((char*)strPath.c_str(), appCachePath))
{
ZLog::ErrorV(">>> Unzip Failed!\n");
return -3;
}
timer.PrintResult(true, ">>> Unzip OK!");
}
//resign and inject libs
timer.Reset();
ZAppBundle bundle;
bool bRet = bundle.SignFolder(&zSignAsset, strFolder, strBundleVersion, strBundleId, strDisplayName, bForce, bWeakInject, bEnableCache);
timer.PrintResult(bRet, ">>> Signed %s!", bRet ? "OK" : "Failed");
if (bRet == false)
{
return -7;
}
if (!strOutputFile.empty())
{
timer.Reset();
string strBaseFolder = bundle.m_strAppFolder;
//move signd file to dir
bool res = temp->moveFile((char *)strBaseFolder.c_str(), (char *)strOutputFile.c_str(), "");
if (!res) {
return -8;
}
timer.PrintResult(true, ">>> Archive OK! (%s)", GetFileSizeString(strOutputFile.c_str()).c_str());
} else {
return -10;
}
delete temp;
gtimer.Print(">>> Done.");
return 0;
}
上面的argv[] 是从外界传过来的参数数组.这里面从里面读取了参数并赋值到当前的变量.
我们看到这个里面初始化了一个ZSignAsset::Init
这个是在openssl.cpp里面定义的一个方法
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);
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(), 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;
}
这个Init方法将.mobleprovison和Entitlements权限文件,读取到data,
ReadFile(strProvisionFile.c_str(), m_strProvisionData);
ReadFile(strEntitlementsFile.c_str(), m_strEntitlementsData);
jvProv["Entitlements"].writePList(m_strEntitlementsData);
随后用P12密码和P12文件进行了证书的校验
X509 *x509Cert = NULL;
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);
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;
}
读取到的密钥信息存储到了全局变量:
m_evpPkey = evpPkey;
m_x509Cert = x509Cert;
在签名的时候是要用到的.
随后读取了IPA文件
char* appCachePath = (char* )fromIpaPath.c_str();
//unzip
bool bEnableCache = true;
string strFolder = GetCanonicalizePath(appCachePath);
接下来是签名和注入动态库
resign and inject libs
ZAppBundle bundle;
bool bRet = bundle.SignFolder(&zSignAsset, strFolder, strBundleVersion, strBundleId, strDisplayName, bForce, bWeakInject, bEnableCache);
timer.PrintResult(bRet, ">>> Signed %s!", bRet ? "OK" : "Failed");
然后是打包文件
if (!strOutputFile.empty())
{
timer.Reset();
string strBaseFolder = bundle.m_strAppFolder;
//move signd file to dir
bool res = temp->moveFile((char *)strBaseFolder.c_str(), (char *)strOutputFile.c_str(), "");
if (!res) {
return -8;
}
timer.PrintResult(true, ">>> Archive OK! (%s)", GetFileSizeString(strOutputFile.c_str()).c_str());
}
base64,common 则是encode文件要用到的.json文件是用的比较广的,用于解析值.
archo读取的是我们的mach-o文件
bool ZArchO::BuildCodeSignature(ZSignAsset *pSignAsset, bool bForce, const string &strBundleId, const string &strInfoPlistSHA1, const string &strInfoPlistSHA256, const string &strCodeResourcesSHA1, const string &strCodeResourcesSHA256, string &strOutput)
上面这个方法则是创建签名文件
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;
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(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;
}
bool ZArchO::Sign(ZSignAsset *pSignAsset, bool bForce, const string &strBundleId, const string &strInfoPlistSHA1, const string &strInfoPlistSHA256, const string &strCodeResourcesData)
这个方法则是对mach-o文件进行签名.
还有注入动态库的
bool ZArchO::InjectDyLib(bool bWeakInject, const char *szDyLibPath, bool &bCreate)
在macho.cpp文件可以看到对文件遍历的签名
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();
}
签名的 InjectDyLib
bool ZMachO::InjectDyLib(bool bWeakInject, const char *szDyLibPath, bool &bCreate)
signing文件则是对签名的解析和创建权限文件的处理,创建签名目录
bool ParseCodeSignature(uint8_t *pCSBase);
uint32_t GetCodeSignatureLength(uint8_t *pCSBase);
bool GetCodeSignatureCodeSlotsData(uint8_t *pCSBase, uint8_t *&pCodeSlots1, uint32_t &uCodeSlots1Length, uint8_t *&pCodeSlots256, uint32_t &uCodeSlots256Length);
bool SlotBuildRequirements(const string &strBundleID, const string &strSubjectCN, string &strOutput);
bool SlotBuildEntitlements(const string &strEntitlements, string &strOutput);
未完待续