结构分析"/>
nib文件结构分析
nib文件结构分析
摘要
nib文件结构字节存储分析;文件字节内容以“NIBArchive”文件魔数开始,包括headers信息,objects、keys、values、classes 这几个部分的信息。如果将nib文件字节内容组织结构类比书的结构,headers部分相当于目录,目录中包含 objects 章,keys章,vlaues章,classes章这些章节的内容起始页位置和章节总页数(结束页位置= 起始页位置+章节总页数)。
xib转为nib
1.通过mac系统的xcode的 ibtool工具 将 xib 转换 nib 文件,命令如下:ibtool --comile output.nib input.xib
;**提示:**如果出现错误: xcode-select: error: tool 'ibtool' requires Xcode, but active developer directory '/Library/Developer/CommandLineTools' is a command line tools instance
则需要切换xcode-select,命令如下:sudo xcode-select -switch /Applications/Xcode.app
2 开发iOS的app时,app中的xib文件会转换为nib文件保存到ipa中。
nib文件字节结构
NIBArchive 文件魔数
理解文件魔数可以类比车牌,车牌的第1位代表不同省份,川:四川,京:北京,渝:重庆等,文件魔数则代表者不同的文件类型。
nib文件字节以“NIBArchive”魔数开头,
文件魔数也可以理解为该文件字节内容结构组织的规则的名字,NIBArchive 魔数表示以这个规则生成的nib文件,nib文件的解析器将会以该魔数来识别nib文件,并按照特定的规则来解析。
压缩后的info.plist文件会bplist00 开头
headers 部分
headers包含数量和header size两个部分,每部分都是4个字节,并且都是小端序(低位存低地址,高位存高地址,与内存增长方向相同)存储,所以数量4位: 00 00 00 01, header size 4位:00 00 00 0A。
header size 字节值 0A 需要通过公式 sections := int((headersize - 1) >> 1)
转换(转换公式的由来不清楚????),结果就是段的数量为4。每个段以8个字节来保存信息,前4个字节保存段的数量,后4个字节保存段的起始位置,这些值也是小端序来存储的
objects 段 count:0x3e, start_address: 0x32
keys 段 count:0x7d, start_address: 0x112
values 段 count:0x126, start_address: 0xbe2
classes 段 count:0x16, start_address: 0x1363
objects 段
该段从文件第0x32个字节开始,总共有0x3e组数据,每组数据由3个属性组成,分别是{ class index,value start index,value size}。
class index : 表示 classes 段中的序号
value start index: 表示 values 段中的开始序号
value size :表示 values 段中从开始序号开始到结束序号直接的数量
也就是所 objects段记录了一个classes段中的值与values段中多个值的关系数据。
值的读取:该段中的每个字节经过加密的,需要进行解密才能获取到真正的值,读取的方式:
如0x80,二进制表示为:1000 0000 (从左向右,位数由高到低),左第一位表示是否继续读取下一个字节,如果左第一位不是1表示继续读取下一个字节,其他位表示为值,如果需要继续读取n个字节,则总值为当前值向左偏移n*7个位+下个字节的值 为总值
读取当前第1个字节的值为val,将val & 0x7f 获取到值rst,将val & 0x80:如果结果不为0,则rst就是属性的值,如果为0,则读取下一个字节val2, 将val2 & 0x7f 获取到值rst2,并将rst向左偏移7个字节后加上rst2为总的结果 R,将val2 & 0x80:如果结果不为0,则总结果R就是属性的值,如果为0,则读取下一个字节。。。 如此递归下去
举例:
从 0x32 位置开始读取接下来3个字节: 0x80 0x80 0x87
0x80: 0x80 & 0x7f=0,0x80&0x80 == 1表示不用读取下一个字节, 所以0x80的值为0 赋给 class index 属性
0x80: 0x80 & 0x7f=0,0x80&0x80 == 1表示不用读取下一个字节, 所以0x80的值为0 赋给 value start index
0x87: 0x87 & 0x7f=7,0x87&0x80 == 1表示不用读取下一个字节, 所以0x87的值为7 赋给 value size 属性
{class index: 0, value start index : 0, value size 7} 表示的含义为:objects有classes段的第0个classname,values段从0开始的后7个values组的值组成
读取一组4个字节:0x81 0x25 0x82 0x81
0x81: 0x81 & 0x7f=1,0x81&0x80 == 1表示不用读取下一个字节, 所以0x81的值为1 赋给 class index 属性
0x25: 0x25 & 0x7f=0x25,0x25&0x80 == 0表示继续读取下一个字节,
0x82: 0x82 & 0x7f=2,0x82&0x80 == 0表示不用读取下一个字节,
所以value start index值为:0x25 + 2 << 7 = 0x125
0x81: 0x81 & 0x7f=1,0x81&0x80 == 1表示不用读取下一个字节, 所以0x81的值为1 赋给 value size 属性
{class index: 1, value start index : 0x125, value size 1}表示的含义为:objects有classes段的第1个classname,values段从0x125开始后1个values组的值组成
keys 段
从0x112开始,总共有0x7d组。每组数据由字符长度(length)和字符编码组成,可以看到keys段里面存储的都是字符串的内容。{length, string}
length:从第一个字节开始读取字符串长度信息,读取方式和objects读取值方式一样需要转换,
string: 获取到长度信息后从存储长度字节的地址开始读取指定长度的字节转换为字符串
举例:0x112开始16进制的字节内容:A2 55 49 56 69 65 77 4C 61 72 67 65 43 6F 6E 74 65 6E 74 53 74 6F 72 65 64 50 72 6F 70 65 72 74 69 65。
0xA2 转换的值为十进制的35,所有从 0xA2 开始35个字节为本组字符串内容:”UIViewLargeContentStoredProperties“,该组为{length:35, string:“UIViewLargeContentStoredProperties”}.
按这个规则所有的字符信息按序的存储再keys段中。
values 段
从 0xbe2开始,总共有0x126组。每组由 keys index ,encode,根据不同字节数存储的value 3个部分组成
keys index : keys 段的序号, 读取方式和objects读取值方式一样需要转换,
encode: 编码,不同的编码表示value字节长度不同并且value所表示的数据的类型不同(注意String类型可能会被解析为数字),目前(2023-8-1)所知的只有以下10个编码,其他的都是错误的
NIB_TYPE_BYTE = 0x00 // 1byteNIB_TYPE_SHORT = 0x01 // 2byteNIB_TYPE_INT = 0x02 // 4byteNIB_TYPE_LONG = 0x03 // 8byteNIB_TYPE_FALSE = 0x04 // 0byteNIB_TYPE_TRUE = 0x05 // 0byteNIB_TYPE_WORD = 0x06 // 4byteNIB_TYPE_DOUBLE = 0x07 // 8byte// string [0]length// if [1]==0x07 为数字,接下来按8个字节来划分读取,且length为17或33// else 为字符串,读取length长度的字符NIB_TYPE_STRING = 0x08 // length byte// 0x09 value = nilNIB_TYPE_OBJECT = 0x0A // 4byte 对象// other is unexpected decoder value
value: 根据不同的编码,value的值包含的字节长度都有所不同
举例:0xbe2开始16进制的字节内容:98 0A 01 00 00 00 C9 0A 2C 00 00 00 85 0A
keys index : 0x98 转换结果24,表示keys段第24个
encode : 0a 表示 nib type object ,他的value 占用4个字节
value: 01 00 00 00 (小端序存储)
第一组为{keys index: 24, encode 10, value : 1}
注意:
如果encode 为NIB_TYPE_STRING时,需要读取下一个encode下一个字节来是否为 0x07(NIB_TYPE_DOUBLE)来判断是否为双精度数字,如果encode下一个字节为 0x07(NIB_TYPE_DOUBLE),则按照每8个为一组数字读取,读取length个字节;
否则按字符类型读取。
classes 段
从0x1363开始,总共有0x16组。每组由字符串长度 length、额外值extraVal,额外值占用的字节数extalValByteSize,字符串的value 组成
每组由字符串长度 length :读取方式和objects读取值方式一样需要转换,
额外值extraVal: 读取方式和objects读取值方式一样需要转换,
额外值占用的字节数extalValByteSize: 额外值extraVal * 4 字节数量
字符串的value:length长度的字节数量
举例:0x1363开始16进制的字节内容:89 80 4E 53 4F 62 6A 65 63 74 00 88 80 4E 53 41 72 72 61 79 00
length : 0x89 转换后为 9 个字节
extraVal : 0x80 转换后为1
extraValByteSize : extral * 4 = 0 * 4 = 0 个字节 (按小端序解析)
value :1 6进制的字节内容:4E 53 4F 62 6A 65 63 74 00 (NSObject.) ,
{length : 9, extraVal: 1, extraValByteSize: 0, value :“NSObject.”}
总结
nib文件二进制结构,像一个字典,headers中保存的sections中的地址信息,类似于字典中的目录,而values中保存的keys的序列号,像是字典中一个字符解释时使用到另一个字符的信息。这种存储的方式在现实生活中的仓储中也是很常见的。现象不同但本质是相同的。
更多推荐
nib文件结构分析
发布评论