randomized layout
在Linux Kernel的结构体声明中,经常会看见最后带了一个__randomize_layout
标识。初次学习,对其机制不太了解,并且网上对其展开描述的内容也不是很多,只有一些英文文档中有介绍。因此,本博客里记录下学习的成果。
结构体随机化
不管是地址随机化,还是结构体随机化,其目的都是增强内核安全性。结构体随机化也是GCC编译器的一个重要特性,使能后将在编译时随机排布结构体中元素的顺序,从而使攻击者无法通过地址偏移进行攻击。(存疑,虽然运行时的结构排布与源码可能不一致,但通过结构元素的地址,仍然可以推断出排布形势,只能防范非自适应的攻击,是否可以这样理解?)
结构体随机化在编译时根据随机数种子打乱排布顺序,但一旦编译完成,结构体内容即确定。(存疑,既然编译时即确定,那么公开发行的二进制版本是可以被反向的,该保护机制是否还有效?以及,调试时,我们能否看到真实的结构体顺序?)
在Linux Kernel中,结构体中存在函数指针的部分是攻击者重点关注的对象。因此,只存储函数指针的结构体,是默认开启结构体随机化的,如果不需要,需要添加__no_randomize_layout
进行排除。另一方面,如果特定结构体希望主动开启保护,需要添加__randomize_layout
标识。
结构体随机化后的差异
- 既然已经开启了结构体随机化,在进行赋值或初始化时,就需要按照元素名称进行赋值,否则会出现非预期结果。(designated initializers)
- 不要对开启了随机化的结构体指针或对象进行强制数据转换,因为内存排布是不可预测的。
- 涉及到远程调用的结构体,如果需要保证结构体内容的一致性,需要添加例外。
- 调试时,根据dump推算结构体内容将极为麻烦,因为每个版本、每个平台的布局都将不同。
- 部分以模块形式添加到Kernel中的驱动,为保持结构体一致性,在编译时需要采用与kernel相同的随机数种子,这带来了极大的安全风险。