0x01 前言
- ASLR 是一种针对缓冲区溢出的安全保护技术,通过对堆、栈、共享库映射等线性区布局的随机化,通过增加攻击者预测目的地址的难度,防止攻击者直接定位攻击代码位置,达到阻止溢出攻击的目的的一种技术
- 目前 ASLR 技术据我所知对3种情况进行了保护,分别是映像基址随机化、堆栈基址随机化和 PEB / TEB 随机化(有论文为参考)。下面来亲自测试一下,看看地址到底会不会变
- 实验环境:Windows 10 ,编译器:C-Free
0x02 映像基址随机化
- 当可执行文件或动态链接库文件被映射到内存时,系统会对其虚拟地址进行随机化(重启系统后会更新地址),示例程序如下(做缓冲区溢出实验的,这里直接拿来用了)
- 这里准备测试7个数据:(1)msvcrt.dll 基地址及大小(2)msvcrt._cexit 的地址及偏移地址(3)msvcrt.singnal 的地址及偏移地址(3)msvcrt.strcpy 地址及偏移地址(4)msvcrt.atexit 地址及偏移地址(5)msvcrt._fileno 地址及偏移地址(6)msvcrt._setmode 地址及偏移地址。其中第一个是动态链接库,其他都是动态链接库里面的函数。
- 测试 msvcrt.dll 的基地址及大小的方法,将编译好的程序放入OD保持程序为运行状态
- 打开 PCHunter 软件,找到刚刚加载的进程,右击在下方显示模块窗口
- 右击显示所有模块
- 可以看到 msvcrt.dll 模块的地址
- 测试动态链接库 msvcrt.dll 中的函数地址及偏移地址的方法,下面是刚刚加载进OD的文件,我们需要找到一个 msvcrt.dll 中存在的函数,这个程序在加载的时候刚好调用了一个(也可以自己编写)
- enter 进入这个函数,可以看到很多 msvcrt.dll 动态链接库里面的函数
- 想看某一个函数的内存中的地址,可以直接 enter 进入这个函数,比如 strcpy,0x775a6400 就是内存中的地址,以上就是测试 scvcrt.dll 动态链接库文件中函数地址的方法
- 之后我测试了10次
- 汇总一下,根据测试的结果可以发现,msvcrt.dll 动态链接库的基地址会变化,但是函数在动态链接库的偏移地址不会变化,基地址大小不变
msvcrt.dll 基地址 | 大小 | |
测试 1 | 0x76180000 | 0xBF000 |
测试 2 | 0x757C0000 | 0xBF000 |
测试 3 | 0x779F0000 | 0xBF000 |
测试 4 | 0x76FD0000 | 0xBF000 |
测试 5 | 0x754B0000 | 0xBF000 |
测试 6 | 0x76CF0000 | 0xBF000 |
测试 7 | 0x74FE0000 | 0xBF000 |
测试 8 | 0x75880000 | 0xBF000 |
测试 9 | 0x77240000 | 0xBF000 |
测试 10 | 0x77520000 | 0xBF000 |
_cexit | 偏移地址 | singnal | 偏移地址 | strcpy | 偏移地址 | |
测试 1 | 0x761e5f30 | 0x65F30 | 0x761d6fe0 | 0x56FE0 | 0x76206400 | 0x86400 |
测试 2 | 0x75825f30 | 0x65F30 | 0x75816fe0 | 0x56FE0 | 0x75846400 | 0x86400 |
测试 3 | 0x77a55f30 | 0x65F30 | 0x77a46fe0 | 0x56FE0 | 0x77a76400 | 0x86400 |
测试 4 | 0x77035f30 | 0x65F30 | 0x77026fe0 | 0x56FE0 | 0x77056400 | 0x86400 |
测试 5 | 0x75515f30 | 0x65F30 | 0x75506fe0 | 0x56FE0 | 0x75536400 | 0x86400 |
测试 6 | 0x76d55f30 | 0x65F30 | 0x76d46fe0 | 0x56FE0 | 0x76d76400 | 0x86400 |
测试 7 | 0x75045f30 | 0x65F30 | 0x75036fe0 | 0x56FE0 | 0x75066400 | 0x86400 |
测试 8 | 0x758e5f30 | 0x65F30 | 0x758d6fe0 | 0x56FE0 | 0x75906400 | 0x86400 |
测试 9 | 0x772a5f30 | 0x65F30 | 0x77296fe0 | 0x56FE0 | 0x772c6400 | 0x86400 |
测试 10 | 0x77585f30 | 0x65F30 | 0x77576fe0 | 0x56FE0 | 0x775a6400 | 0x86400 |
atexit | 偏移地址 | _fileno | 偏移地址 | _setmode | 偏移地址 | |
测试 1 | 0x761d6ca0 | 0x56CA0 | 0x761f3130 | 0x73130 | 0x761c9d70 | 0x49D70 |
测试 2 | 0x75816ca0 | 0x56CA0 | 0x75833130 | 0x73130 | 0x75809d70 | 0x49D70 |
测试 3 | 0x77a46ca0 | 0x56CA0 | 0x77a63130 | 0x73130 | 0x77a39d70 | 0x49D70 |
测试 4 | 0x77026ca0 | 0x56CA0 | 0x77043130 | 0x73130 | 0x77019d70 | 0x49D70 |
测试 5 | 0x75506ca0 | 0x56CA0 | 0x75523130 | 0x73130 | 0x754f9d70 | 0x49D70 |
测试 6 | 0x76d46ca0 | 0x56CA0 | 0x76d63130 | 0x73130 | 0x76d39d70 | 0x49D70 |
测试 7 | 0x75036ca0 | 0x56CA0 | 0x75053130 | 0x73130 | 0x75029d70 | 0x49D70 |
测试 8 | 0x758d6ca0 | 0x56CA0 | 0x758f3130 | 0x73130 | 0x758c9d70 | 0x49D70 |
测试 9 | 0x77296ca0 | 0x56CA0 | 0x772b3130 | 0x73130 | 0x77289d70 | 0x49D70 |
测试 10 | 0x77576ca0 | 0x56CA0 | 0x77593130 | 0x73130 | 0x77569d70 | 0x49D70 |
0x03 堆栈随机化
- 程序启动时,系统会随机选择堆栈的基址,从而导致内存中各种变量地址发生改变(程序每次启动前)
- 首先看一下如何开启程序的ASLR防护,这个貌似只有微软的编译器才有的功能,在VS编译器中新建一个项目(我的是 VS2017)
- 将随机基址改为是即可,那么怎么看一个可执行文件是否受到 ASLR 保护呢,可以用 PEview 这个工具看文件头中是否有 IMAGE_FILE_RELOCS_STRIPPED 这个标志,有表示没有受到 ASLR 保护,没有则表示收到了 ASLR 的保护
- 这个是没有的
- 这个是有的
- 下面测试一下受到了 ASLR 保护的程序堆栈地址到底会不会变化,OD载入,到达程序的真正入口 OEP
- 看一下此时堆栈地址
- 之后重复 10 次,对比一下,可以看出入口堆栈地址防护较好,变化很大,随机性非常强
程序入口时的堆栈地址(有 ASLR) | 程序入口时的堆栈地址(无 ASLR) | |
测试 1 | 0x00B7F76C | 0x243FF24 |
测试 2 | 0x0137F750 | 0x243FF24 |
测试 3 | 0x00BDF7FC | 0x243FF24 |
测试 4 | 0x00DAF8C0 | 0x243FF24 |
测试 5 | 0x0077F7B0 | 0x243FF24 |
测试 6 | 0x0057FC88 | 0x243FF24 |
测试 7 | 0x009FF834 | 0x243FF24 |
测试 8 | 0x012FF848 | 0x243FF24 |
测试 9 | 0x008FFA34 | 0x243FF24 |
测试 10 | 0x0075FDD0 | 0x243FF24 |
0x04 PEBTEB随机化
- 进程环境块(PEB)和线程环境块(TEB)随机化,在程序每次启动时(这个和编译时有没有加上 ASLR 防护没有关系,好像是系统默认的,加不加都会随机)
- 利用 Windbg 查看 PEB 和 TEB 的地址(Windbg32,Windbg64)
- 选择 File -> Open Executable,选择要查看的文件
- 在最下面的命令行中输入 !peb 和 !teb 即可查询
- 测试 5 次汇总一下,变化并不是很大
PEB地址 | TEB地址 | |
测试 1 | 0x00000000002FD000 | 0x00000000002FF000 |
测试 2 | 0x0000000000352000 | 0x0000000000354000 |
测试 3 | 0x000000000039A000 | 0x000000000039C000 |
测试 4 | 0x000000000038A000 | 0x000000000038C000 |
测试 5 | 0x0000000000302000 | 0x0000000000304000 |
- 对于 ASLR 的全部测试到此结束