Android11开始使用
保护措施
隔离
分primary和secondarychunks 前者放在专门的堆内存区域 (anon:scudo:primary]) 后者使用自己的内存区域
primary chunk根据大小范围从对应的区域申请,堆溢出只能溢出对应class内存区域内的chunks scudo中 这些大小范围根据class ID确定,class ID由size确定
metadata存储在和chunk不同的内存区域,存在0权限的间隔页,即guard pages 防止跨内存区域读写
0x75ff4cf000 0x760f492000 ---p ffc3000 0 [anon_75ff4cf]
随机化
同一个内存区域中的chunk被申请时存在随机偏移 当一块区域被mapped,若干可能被申请到的地址被放进所谓的"TransferBatch",这些地址从TransferBatch中返回的顺序是随机的
保护
chunk头如下

The fields and corresponding sizes in the Scudo chunk header. The OriginOrWasZeroed field indicates the origin of the chunk, e.g., malloc or new. The SizeOrUnusedBytes field indicates the exact chunk size. Offset is filled with zeros.
eg
in use => state = 1 freed => state = 0
Checksum Offset SizeOrUnusedBytes Origin.. State ClassId 1100011100110111 0000000000000000 00000000000000000110 00 01 00000001 0111101001010001 0000000000000000 00000000000000010000 00 01 00000001
classID存储chunk的class ID state表示chunk是使用中还是被释放 为保护该chunk头,scudo在checksum字段存储了一个截断的chunk头各字段的crc32 checksum。checksum使用chunk地址,chunk头和一个32bit cookie值计算,cookie值是程序开始的时候随机生成的。具体计算方式如下
short checksum(long address, long header, int cookie){ int intermediate = CRC32(cookie, address); intermediate = CRC32(intermediate, header); return = (short) (intermediate & (intermediate >> 16)) & 0xffff; }
任何时候scudo和一个chunk交互,都重新计算checksum并与存储的checksum比较 通过state字段标记使用中还是释放,并根据此检测double free
classID和大小对应关系

分离
Secondary chunk有和primary chunk一样的chunk头,但classID是0
另外,secondary chunk有一个扩展头,从返回指针-0x40处开始,存储了链接已分配的secondary chunk链表指针,并存储了mapping的基地址和大小,分别带有和不带有保护页(guard pages),这些信息保存在 MapBase、MapSize、CommitBase 和 CommitSxize 中。
扩展头如下

分析利用
如果能泄漏TransferBatch或用于洗牌TransferBatch的seed,则可以知道scudo之后会申请的chunk 对于保护protect(chunk头相关),攻击者需要知道目标chunk地址和cookie来正确计算checksum
释放的chunk能够立刻被再次申请回来
unlink
更改classID为0,释放时利用secondary chunk的extend header指针进行unlink任意地址写地址