一、堆栈保护
在 gcc 编译器编译 C 语言代码时 , 如果添加了 -Wl,-z,nostackprotector
选项 , 就是构建一个没有 堆栈保护 的 ELF 格式的 共享对象 ( SO 文件 ) ;
堆栈保护 指的是 栈溢出保护 , Canary 值 ;
执行 readelf -sW example.so
命令 , 可以查询动态库是否启用了 堆栈保护 ;
二、gcc 编译选项 -fstack-protector
-fstack-protector
是 gcc 编译器 的 增强 堆栈保护的 选项 , 该选项可以增强程序的安全性 , 特别是对抗堆栈缓冲区溢出攻击 ;
" 堆栈保护 " 选项 -fstack-protector
的工作原理是在编译代码时插入一些保护代码 , 检测是否有堆栈溢出的发生 :
- Canary 值 : 在函数的 栈帧 中插入一个特殊的 Canary 值 , 该值 放置在 函数的局部变量 和 控制数据 之间 , 用于检测堆栈缓冲区溢出 ;
- 栈帧检查 : 函数返回之前 , 检查 Canary 值是否被修改 , 如果被修改 , 说明发生了堆栈溢出 , 直接终止程序 ;
三、解决方案
1、交叉编译动态库时设置 -fstack-protector 参数
在交叉编译动态库时 , 设置 -fstack-protector
参数 ;
这样编译出来的动态库 利用堆栈缓冲区溢出 的难度会增加 ;
2、Android.mk 配置
在 Android.mk 脚本中配置
代码语言:javascript复制LOCAL_CFLAGS := -Wall -O2 -U_FORTIFY_SOURCE -fstack-protector-all
参数 ,
-Wall
: 开启所有警告 ;-O2
: 使用优化级别 2 , 进行代码优化 ;-U_FORTIFY_SOURCE
: 取消 _FORTIFY_SOURCE 的定义 , 这是用于增强安全性的宏定义 ;-fstack-protector-all
: 启用所有 堆栈保护 措施 ;
完整配置示例如下 :
代码语言:javascript复制# Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# 编译选项
LOCAL_CFLAGS := -Wall -O2 -U_FORTIFY_SOURCE -fstack-protector-all
# 指定源文件
LOCAL_SRC_FILES := example.c
# 设置 .got.plt 的只读属性
LOCAL_LDFLAGS := -Wl,-z,relro,-z,now
# 指定生成的共享对象名称
LOCAL_MODULE := libexample
include $(BUILD_SHARED_LIBRARY)
3、CMakeLists.txt 配置
在 CMakeLists.txt 中配置如下编译选项 :
代码语言:javascript复制set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -O2 -U_FORTIFY_SOURCE -fstack-protector-all")
-Wall
: 开启警告 ;-O2
: 启用优化级别 2 ;-U_FORTIFY_SOURCE
: 取消 _FORTIFY_SOURCE 的定义 , 这是用于增强安全性的宏定义 ;-fstack-protector-all
: 启用所有 堆栈保护 措施 ;
完整配置示例如下 :
代码语言:javascript复制cmake_minimum_required(VERSION 3.0)
project(example_project C)
# 设置编译选项
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -O2 -U_FORTIFY_SOURCE -fstack-protector-all")
# 添加可执行文件或共享对象
add_executable(example_executable example.c)
# 设置链接选项
target_link_options(example_executable PRIVATE -Wl,-z,relro,-z,now)