这两天看了一下ldid的源代码,对codesign的一些理解,简单记录一下。
1、数字签名。简单来说就是HASH算法+私钥加密。从而可以确保文件的来源以及完整性。mac os对于程序的codesign并不只是简单的对整个文件进行数字签名,而是对程序文件以页大小为单位进行数字签名,加上对codesign部分的各个blob进行数字签名(根据ldid的代码,没有对codedirectory blob进行数字签名?)。
2、codesign功能。确保文件的来源以及完整性只是codesign的其中一个(最主要的)功能,签名者还可以加上各种requirement,验证者(操作系统和各个程序)可以按需使用(具体可以查看下面参考文档CodeSigningGuide)。另一个功能就是包含entitlement文件了。
3、codesign数据。如果程序被codesign了,程序会多出一个codesign load command,以及对应的codesgin数据。codesign load command里有表明codesign数据在程序文件里的偏移量(dataoff)和大小(datasize)。codesign数据,简单来说就是由一个“索引块”和多个”功能块“组成,每个“功能块”表示某一种用途。
SuperBlob(索引)
–CodeDirectory(程序的bundle id,各个数字签名)
–Requirements(这里应该就是苹果文档里对应的internal requirements,各个requirement使用codesign requirement language表示,最终内容是text->binary的形式,即compiled后的内容)
–Entitlements(这里就是entitlement文件了)
具体各个blob的结构表示,可以参考后面的codesign.h。
4、其他一些点。
1)ldid里对各个部分使用sha1的hash,没有使用私钥加密。sha1出来的hash位数为20个字节。
2)ldid里,重签步骤里还会修改__LINKEDIT segment的filesize。这是为什么呢?应该是因为:重签会修改到codesign load command和codesign数据部分。那就说明codesing数据部分是属于linkedit segment的,因此,需要修改linkedit segment的大小。从这里也可以解释为什么codesign load command的结构体是linkedit data command(cmd+cmdsize+dataoff+datasize)了,因为codesing数据就是其中一种linkedit数据。
3)ldid里,重签时的requirement的内容\xfa\xde\x0c\x01\x00\x00\x00\x0c\x00\x00\x00\x00这12个字节是什么意思?当时猜测这个应该是codesign requirement language的compiled后的数据。后来通过使用csreq -r file -t(file内容就为这12个字节),命令可以成功执行,但是,返回/* no requirements in set */,表示没有内容。那么,可能就是最小的没有requirement的表示吧。简单来说,就是4个字节是internal requirements blob的magic,4个字节是blob长度,4个字节是内容(因为没有内容所以全为\x00)。
4)ldid重签,codesgin数据部分只会包括CodeDirectry, Requirements(上面提到的那12个字节)以及entitlement文件(如果有指定的话)。
5)结合codesign.h里的数据结构的定义,ldid的源代码,以及hex编辑器来理解。
5、一些命令。
查看codesign load command
otool -l binary | grep -A 5 SIGNATURE
查看签名数据内容
codesign -dvvv binary
查看compiled codesign requirement language的文本内容
csreq -r file -t
查看entitlement内容
ldid -e binary
修改entitlement内容
ldid -S***entitlement.xml*** binary
6、ldid一些代码片段。
1)显示entitlement文件内容。
if (flag_e) {
_assert(signature != NULL);
uint32_t data = mach_header.Swap(signature->dataoff);
uint8_t *top = reinterpret_cast(mach_header.GetBase());
uint8_t *blob = top + data;
struct SuperBlob *super = reinterpret_cast<struct SuperBlob *>(blob);
for (size_t index(0); index != Swap(super->count); ++index)
if (Swap(super->index[index].type) == CSSLOT_ENTITLEMENTS) {
uint32_t begin = Swap(super->index[index].offset);
struct Blob *entitlements = reinterpret_cast<struct Blob *>(blob + begin);
fwrite(entitlements + 1, 1, Swap(entitlements->length) - sizeof(struct Blob), stdout);
}
}
2)normal部分的签名(即除去codesign数据部分的文件内容的签名,以页大小的单位签)(属于CodeDirectory部分的数据)
// 这里是normal部分的签名!!
if (pages != 1)
for (size_t i = 0; i != pages - 1; ++i)
sha1(hashes[i], top + 0x1000 * i, 0x1000);
if (pages != 0)
// 最后一个特殊处理,因为可能不满一个页面大小
sha1(hashes[pages - 1], top + 0x1000 * (pages - 1), ((data - 1) % 0x1000) + 1);
3)special部分的签名(即codesign数据部分的文件内容的签名,以blob为单位签)(属于CodeDirectory部分的数据)
for (size_t index(0); index != count; ++index) {
uint32_t type = Swap(super->index[index].type);
if (type != 0 && type <= special) {
uint32_t offset = Swap(super->index[index].offset);
struct Blob *local = (struct Blob *) (blob + offset);
// 这里是specail部分的签名!!
// 长度为blob的长度
sha1((uint8_t *)(hashes - type), (uint8_t *)local, Swap(local->length));
}
}
4)签名个数(属于CodeDirectory部分的数据)
uint32_t special = xmld == NULL ? CSSLOT_REQUIREMENTS : CSSLOT_ENTITLEMENTS;
// number of special hash slots
// special部分的签名slot
directory->nSpecialSlots = Swap(special);
uint8_t(*hashes)[20] = reinterpret_cast20]>(blob + offset);
memset(hashes, 0, sizeof(*hashes) * special);
offset += sizeof(*hashes) * special;
// 注意,签名是normal部分+special部分的!!!!
hashes += special;
uint32_t pages = (data + 0x1000 - 1) / 0x1000;
// number of ordinary (code) hash slots
// normal部分的签名slot
directory->nCodeSlots = Swap(pages);
5)bundle id(属于CodeDirectory部分的数据)
// offset of identifier string
directory->identOffset = Swap(offset - begin);
strcpy(reinterpret_cast<char *>(blob + offset), name);
offset += strlen(name) + 1;
参考:
http://gitweb.saurik.com/ldid.git
http://www.saurik.com/id/8
http://www.opensource.apple.com/source/xnu/xnu-3248.20.55/bsd/sys/codesign.h
https://developer.apple.com/library/mac/documentation/Security/Conceptual/CodeSigningGuide/Introduction/Introduction.html