前言
这篇内容其实是来自于文章AddressSanitizer: A Fast Address Sanity Checker 的阅读笔记。
其实它可能跟iot
关系不是很大,但是他和我做得iot相关的毕设关系很大(😀。
我的毕设似乎是要往arm架构上迁移x86的一些安全检测技术(比如AddressSanitizer,它是现在普遍应用与软件安全检测的一种技术)。
然而,AddressSanitizer到底是什么个东西,我也不是很懂。
所以看一下这篇论文还是很重要的.
AddressSanitizer composition
AddressSanitizer
,简称asan
,构建起这个技术的组成有3种。即shadow memory(影子内存),插桩(instrumentation),检测分配器(debug allocator)
1. Shadow Memory
shadow memory
,其实是献祭部分内存,表示其他内存状态的一种方法。
比如,用位于0x4000的内存的值,用来表示0x8000位置的内存的状态。
2. Instrumentation
插桩
,直白地说,就说往源码或二进制代码里插入一些检测方法,帮助用户截获程序异常信息。
3. Debug Allocator
要想实现这个技术,需要有一个专门为其定制的Allocator
,因为要做一些heap检测时,需要hook类似于malloc
和free
函数,使得其能够多分配一些内存存储redzone
,释放时不会立即释放。
AddressSanitizer Algorithm
1. shadow memory
AddressSanitizer
用的shadow memory
映射的规律是这种形式
- Addr 表示实际的内存虚拟地址
- Scale 表示伸缩的范围,因为Asan用 1byte 表示 8个byte内存状态,因此Scale 为3
- offset 表示一个偏移,这是基于一个实际意义的考量,因为 shadow memory 的内存位置 不能和原本已使用的内存空间冲突,所以我们需要找到一块内存的位置,保证程序不会使用到它。这就是偏移的用处
关于offset
的设置,实际上shadow memory
用到的内存范围为offset
~ offset+max(系统最大内存)/8
。因为需要保证不会占用程序启动时需要的内存区域,offset在32
位下通常是Offset = 0x20000000($2^{29}$). 而在64
位下,通常是Offset = 0x0000100000000000($2^{44}$)
在编译器选项-fPIE/-pie
(compiler flags on Linux) 下,通常使用offset=0
的形式(因为0地址区间不会被使用到)
值得注意的一点是,因为 shadow memory
映射了整个虚拟内存,当然会出现影子内存映射到映射内存的位置,我们称其为bad
区域,对此的处理是,将bad
区域设置为不可访问区域。
关于影子内存表示的值的意义
- 0 表示 8个字节均可访问
- 1,2…k…7 表示前k个字节均可访问
- 负数表示前 8个字节均不可访问
实际上scale可以是1~7的任意值,scale越大,shadow memory占据的位置越小(1/2^N^),然而 使用的 redzong区域会更大(2^N^)
2. Instrumentation
如果是进行一个8字节的访问,会插入以下的公式 如果是 1,2,4字节访问,会使用以下公式
插桩设置会放置在llvm optimization后(因为优化后的内存访问会减少,减少插桩的数量,提高程序运行效率)
因为插桩默认是 访问几个字节就几字节对齐,所以会导致有些越界访问不会被检查到。
像这种风格的代码,*u=(int *)((char * )a+6)时,不会发生触发crash。因为在检测时,他是符合规则 *ShadowAddr=0
的
值得一提的是,虽然有漏报,但是没有误报情况
3. Run-time Library
这个东西其实是用来管理 shadow memory
的(比如初始化),并且对malloc
函数,free
函数进行一些hook操作,使其在申请/释放前,先对shadow memory
的相应位置进行值的更改。
对于malloc
,会申请更大的内存,存放redzone
。
对于内存分配器allocator
的操作,以下这个解释比较形象
对于free
,并不会立即释放这个内存,而且把它挂在一个空链表中,被称作quarantine
,即隔离。这个空链表遵循FIFO
规则,因此,当空链表已经满了后,发生的 use-after-free
就不会什被check到。
4. Stack And Globals
5. Thread
实践
AddressSanitizer
已经被集成到了llvm
上,下载llvm
后即可使用。
使用文档如下
https://github.com/google/sanitizers/wiki/AddressSanitizer
实际跑个代码试试
#include <stdlib.h>
int main() {
char *x = (char*)malloc(10 * sizeof(char*));
free(x);
return x[5];
}
clang -fsanitize=address test.c -o test_asan //编译一个有asan的
clang test.c -o test //原始的
运行 有asan的会出现如下内容:
ida查看
再来看看原版的