模拟shadow_stack保护(可编译选项)
认识shadow_stack 什么是shadow stack Shadow Stack(影子栈)是一种由硬件或操作系统直接管控的只读备份栈,专门用于同步存储主栈中的返回地址。其核心机制是:当程序执行CALL
指令时,返回地址会同时压入主执行栈和影子栈;而执行RET
指令时,系统会校验主栈弹出的返回地址与影子栈中的备份是否一致 —— 若不一致则立即终止执行,以此防御 ROP这类通过篡改主栈返回地址、拼接无害代码片段(gadget)来劫持程序执行流的攻击。
验证某程序是否在编译时启用 CET:
1 2 3 4 $ readelf -n <application> | grep -a SHSTK Properties: x86 feature: IBT, SHSTK$ readelf -n <application> | grep -a IBT Properties: x86 feature: IBT, SHSTK
验证当前程序(cat)运行时是否已启用 Shadow Stack:(通常不会启用)
1 2 $ cat /proc/self/status | grep shstk $ cat /proc/<pid>/status | grep shstk # pid 为 <pid> 的程序是否启用 Shadow Stack
HSTK 的主要逻辑就在 arch/x86/kernel/shstk.c
引用自[初探 Shadow Stack](初探 Shadow Stack - RiK’s Blog )
COOP攻击 Shadow Stack 启用后,传统的 ROP 等技术失效,但并非无法做到类似链式 gadgets 的任意代码执行。可以采用一种叫“伪面向对象编程”(Counterfeit Object-Oriented Programming / COOP)的新技术,其基本思路是在类似 C++ 的面向对象语言中通过修改虚函数表为 vfgadgets 来达到类似 ROP 的效果。vfgadget 分为 Main Loop Gadget(将其他 gadgets 串联起来)、Argument Loader Gadget(类似 ROP 中的 pop rdi; ret;
)、Invoker Gadget(类似 ROP 中的 system
)、Collector Gadget(存储 Invoker 的返回值)。
大小 任务的影子堆栈从内存中分配到 MIN(RLIMIT_STACK, 4 GB) 的固定大小。
信号 启用shaow_stack后,执行call时的函数的返回地址会被同时push到shadow_stack上。
随后在ret时,内核会验证ssp(shadow_stack的栈顶指针)与call stack的返回地址。
关于用户态simulate_shadow_stack
的设想 演示poc 由于笔者水平有限,所以只能尝试在用户态上分配一块内存来模拟shadow_stack
以下是笔者写的一个写入源码里面演示poc(初版)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <stdarg.h> #include <sys/mman.h> void *shadow_stack;void **ssp;void **sbp;void create_stack () { shadow_stack = mmap(NULL , 4096 , PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1 , 0 ); if (shadow_stack == MAP_FAILED) { perror("mmap failed" ); exit (1 ); } sbp = (void **)((char *)shadow_stack + 4096 ); ssp = sbp; printf ("Shadow stack created:\n" ); printf (" Base address: %p\n" , shadow_stack); printf (" Stack base (high): %p\n" , sbp); printf (" Stack top (current): %p\n" , ssp); printf (" Stack size: 4096 bytes\n" ); }void push_addr () { void *ret_addr = __builtin_return_address(1 ); if (ssp <= (void **)shadow_stack) { printf ("Shadow stack overflow!\n" ); exit (1 ); } ssp--; *ssp = ret_addr; printf ("Pushed return address: %p to stack position: %p\n" , ret_addr, ssp); }void check_addr () { void *curr_addr = __builtin_return_address(1 ); if (ssp >= sbp) { printf ("Shadow stack underflow!\n" ); exit (1 ); } void *saved_addr = *ssp; ssp++; printf ("Checking: current=%p, saved=%p\n" , curr_addr, saved_addr); if (curr_addr != saved_addr) { printf ("Address mismatch detected! Potential ROP attack!\n" ); printf ("Expected: %p\n" , saved_addr); printf ("Found: %p\n" , curr_addr); exit (1 ); } else { printf ("Address check passed\n" ); } }void __printf(const char *format, ...) { push_addr(); va_list args; va_start(args, format); vprintf (format, args); va_end(args); check_addr(); }void __gets(char *buf) { push_addr(); gets(buf); check_addr(); }int main () { char buf[16 ]; create_stack(); push_addr(); __printf("Hello, World!\n" ); __gets(buf); check_addr(); return 0 ; }
第二版poc:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <stdarg.h> #include <sys/mman.h> #include <string.h> #define FUNC_ENTRY() do { \ void *__ret_addr = __builtin_return_address(0); \ shadow_stack_push(__ret_addr); \ } while(0) #define FUNC_EXIT() do { \ void *__ret_addr = __builtin_return_address(0); \ shadow_stack_check(__ret_addr); \ } while(0) #define PROTECTED_FUNC_BEGIN() FUNC_ENTRY() #define PROTECTED_FUNC_END() FUNC_EXIT() void *shadow_stack;void **ssp;void **sbp;void shadow_stack_init () { shadow_stack = mmap(NULL , 4096 , PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1 , 0 ); if (shadow_stack == MAP_FAILED) { perror("[system]Shadow stack init failed" ); exit (1 ); } sbp = (void **)((char *)shadow_stack + 4096 ); ssp = sbp; printf ("[system]Shadow stack protection enabled\n" ); printf ("Shadow stack created:\n" ); printf (" Base address: %p\n" , shadow_stack); printf (" Stack base (high): %p\n" , sbp); printf (" Stack top (current): %p\n" , ssp); printf (" Stack size: 4096 bytes\n" ); }void shadow_stack_push (void *ret_addr) { if (ssp <= (void **)shadow_stack) { printf ("[system]Shadow stack overflow!\n" ); exit (1 ); } ssp--; *ssp = ret_addr; }void shadow_stack_check (void *curr_addr) { if (ssp >= sbp) { printf ("[system]Shadow stack underflow!\n" ); exit (1 ); } void *saved_addr = *ssp; ssp++; if (curr_addr != saved_addr) { printf ("[system]SECURITY ALERT: Return address tampering detected!\n" ); printf ("Expected: %p\n" , saved_addr); printf ("Found: %p\n" , curr_addr); printf ("Possible ROP/JOP attack blocked!\n" ); exit (1 ); } }void vulnerable_function () { PROTECTED_FUNC_BEGIN(); char buffer[16 ]; gets(buffer); PROTECTED_FUNC_END(); }int main () { shadow_stack_init(); PROTECTED_FUNC_BEGIN(); vulnerable_function(); PROTECTED_FUNC_END(); return 0 ; }
初版脚本 写了一个脚本,把模拟影子栈的那部分源码放到了脚本里面,然后利用gcc里面的-finstrument-functions选项来帮助完成插入。利用这个脚本,任意一个c源码都可以快速加上模拟影子栈保护
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 #!/bin/bash SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]} " ) " && pwd) " SHADOW_RUNTIME_DIR="$SCRIPT_DIR " ORIGINAL_GCC="gcc" RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' show_help () { echo "Shadow Stack Compiler Wrapper" echo "用法: $0 [选项] 源文件..." echo "" echo "Shadow Stack 选项:" echo " -shadow 启用影子栈保护" echo " -shadow-debug 启用影子栈保护并显示调试信息" echo " -shadow-silent 启用影子栈保护但不显示系统信息" echo " -shadow-help 显示此帮助信息" echo "" echo "其他选项将直接传递给 GCC" echo "" echo "示例:" echo " $0 -shadow test.c -o test" echo " $0 -shadow-debug -g test.c -o test" } ENABLE_SHADOW=0 SHADOW_DEBUG=0 SHADOW_SILENT=0 GCC_ARGS=() SOURCE_FILES=()while [[ $# -gt 0 ]]; do case $1 in -shadow-help) show_help exit 0 ;; -shadow-debug) ENABLE_SHADOW=1 SHADOW_DEBUG=1 shift ;; -shadow-silent) ENABLE_SHADOW=1 SHADOW_SILENT=1 shift ;; -shadow) ENABLE_SHADOW=1 shift ;; *.c|*.cpp|*.cc|*.cxx) SOURCE_FILES+=("$1 " ) GCC_ARGS+=("$1 " ) shift ;; *) GCC_ARGS+=("$1 " ) shift ;; esac done if [[ $ENABLE_SHADOW -eq 1 ]]; then echo -e "${GREEN} [Shadow Stack]${NC} 启用影子栈保护" TEMP_DIR=$(mktemp -d) trap "rm -rf $TEMP_DIR " EXIT MODIFIED_FILES=() for src_file in "${SOURCE_FILES[@]} " ; do if [[ -f "$src_file " ]]; then base_name=$(basename "$src_file " ) temp_file="$TEMP_DIR /$base_name " echo -e "${BLUE} [Shadow Stack]${NC} 处理源文件: $src_file " { head -n 10 "$src_file " | grep -E '^#include' cat << 'EOF' // ==================== Shadow Stack Runtime ==================== // 声明gets函数 char *gets(char *s); // Shadow stack runtime void *shadow_stack; void **ssp; void **sbp; int shadow_stack_enabled = 0; static int in_hook = 0; // 简单的strlen实现,避免调用库函数 static size_t safe_strlen(const char *s) { size_t len = 0; while (s[len]) len++; return len; } // 安全的写入函数,避免调用被插桩的库函数 static void safe_write(const char *msg) { write(STDERR_FILENO, msg, safe_strlen(msg)); } __attribute__((constructor)) void shadow_stack_init () { if (shadow_stack_enabled) return ; shadow_stack = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (shadow_stack == MAP_FAILED) { perror("[system]Shadow stack init failed" ); exit (1); } sbp = (void **)((char *)shadow_stack + 4096 ); ssp = sbp; shadow_stack_enabled = 1 ; //printf("[system]Shadow stack protection enabled\n"); } __attribute__((destructor)) void shadow_stack_cleanup () { if (shadow_stack_enabled) { shadow_stack_enabled = 0; // 先禁用,防止清理过程中的递归调用 if (shadow_stack != NULL) { munmap(shadow_stack, 4096); shadow_stack = NULL; } // 不在这里使用printf ,因为可能导致段错误 } } void __fshadow_stack_push(void *ret_addr) { if (!shadow_stack_enabled || shadow_stack == NULL) return ; if (ssp <= (void **)shadow_stack) { safe_write("[system]Shadow stack overflow!\n" ); exit (1); } ssp--; *ssp = ret_addr; } void __fshadow_stack_check(void *curr_addr) { if (!shadow_stack_enabled || shadow_stack == NULL) return ; // 如果影子栈为空,直接返回而不报错(可能是程序正常退出) if (ssp >= sbp) { return ; // 静默处理空栈情况 } void *saved_addr = *ssp; ssp++; if (curr_addr != saved_addr) { safe_write("\n=== SHADOW STACK SECURITY ALERT ===\n" ); safe_write("Return address tampering detected!\n" ); safe_write("Possible ROP/JOP attack blocked!\n" ); safe_write("Process terminated for security.\n" ); safe_write("===================================\n" ); _exit(1); } } // 编译器会自动插入这些调用 __attribute__((no_instrument_function)) void __cyg_profile_func_enter(void *this_fn, void *call_site) { if (!shadow_stack_enabled || in_hook) return ; in_hook = 1; // 设置标志,防止递归 if (call_site != NULL && call_site != (void *)0x1) { safe_write("[DEBUG] Function enter - pushing to shadow stack\n" ); } else { safe_write("[DEBUG] Function enter - skipping (invalid call_site)\n" ); } // 只有当call_site有效时才推入影子栈 if (call_site != NULL && call_site != (void *)0x1 && shadow_stack != NULL) { __fshadow_stack_push(call_site); } in_hook = 0; // 清除标志 } __attribute__((no_instrument_function)) void __cyg_profile_func_exit(void *this_fn, void *call_site) { if (!shadow_stack_enabled || in_hook) return ; in_hook = 1; // 设置标志,防止递归 if (call_site != NULL && call_site != (void *)0x1) { safe_write("[DEBUG] Function exit - checking shadow stack\n" ); } else { safe_write("[DEBUG] Function exit - skipping (invalid call_site)\n" ); } // 只有当call_site有效时才检查影子栈 if (call_site != NULL && call_site != (void *)0x1 && shadow_stack != NULL) { __fshadow_stack_check(call_site); } in_hook = 0; // 清除标志 } // ==================== End Shadow Stack Runtime ==================== EOF tail -n +1 "$src_file " | grep -v '^#include' } > "$temp_file " MODIFIED_FILES+=("$temp_file " ) fi done NEW_GCC_ARGS=() for arg in "${GCC_ARGS[@]} " ; do found=0 for i in "${!SOURCE_FILES[@]} " ; do if [[ "$arg " == "${SOURCE_FILES[$i]} " ]]; then NEW_GCC_ARGS+=("${MODIFIED_FILES[$i]} " ) found=1 break fi done if [[ $found -eq 0 ]]; then NEW_GCC_ARGS+=("$arg " ) fi done SHADOW_FLAGS=( "-finstrument-functions" ) if [[ $SHADOW_DEBUG -eq 1 ]]; then SHADOW_FLAGS+=("-DSHADOW_DEBUG" ) echo -e "${YELLOW} [Shadow Stack]${NC} 启用调试模式" fi if [[ $SHADOW_SILENT -eq 1 ]]; then SHADOW_FLAGS+=("-DSHADOW_SILENT" ) echo -e "${YELLOW} [Shadow Stack]${NC} 启用静默模式" fi FINAL_COMMAND=( "$ORIGINAL_GCC " "${SHADOW_FLAGS[@]} " "${NEW_GCC_ARGS[@]} " ) echo -e "${BLUE} [Shadow Stack]${NC} 执行编译命令:" echo -e "${BLUE} [Shadow Stack]${NC} ${FINAL_COMMAND[*]} " "${FINAL_COMMAND[@]} " compile_result=$? if [[ $compile_result -eq 0 ]]; then echo -e "${GREEN} [Shadow Stack]${NC} 编译成功,影子栈保护已启用" else echo -e "${RED} [Shadow Stack]${NC} 编译失败" fi exit $compile_result else exec "$ORIGINAL_GCC " "${GCC_ARGS[@]} " fi
安全版本(2025/9/9改) 上一版本的shadow_stack漏洞过多,比如可以通过其他漏洞修改shadow_stack_enable这个全局变量来关闭保护(可以用来出题),直接修改影子栈等问题。这个版本与canary类似引入了一个储存在fs:0x48段的一个随机key值,并把key与储存在shadow_stack上的返回地址进行加密,在比较的时候再引用key值来解密,这样就可以起到防止修改shadow_stack段的作用,同时修复了修改shadow_stack_enable这个全局变量来关闭保护的bug,现在只在保护初始化的时候使用这个全局变量。
poc 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/mman.h> #include <fcntl.h> char *gets (char *s) ;void *shadow_stack;void **ssp;void **sbp;static int in_hook = 0 ;static size_t safe_strlen (const char *s) { size_t len = 0 ; while (s[len]) len++; return len; }static void safe_write (const char *msg) { write(STDERR_FILENO, msg, safe_strlen(msg)); }static inline void write_fs_48 (unsigned long value) { __asm__ volatile ("movq %0, %%fs:0x48" : : "r" (value)) ; }static inline unsigned long read_fs_48 () { unsigned long key; __asm__ volatile ("movq %%fs:0x48, %0" : "=r" (key)) ; return key; }unsigned long read_urandom_key () { int fd; unsigned long key = 0 ; ssize_t bytes_read; fd = open("/dev/urandom" , O_RDONLY); if (fd == -1 ) { safe_write("[ERROR] 无法打开/dev/urandom\n" ); exit (-1 ); } bytes_read = read(fd, &key, sizeof (key)); if (bytes_read != sizeof (key)) { safe_write("[WARNING]key读取出错\n" ); exit (-1 ); } close(fd); return key; }unsigned long generate_and_store_shadow_key () { static int initialized = 0 ; if (!initialized) { unsigned long key = read_urandom_key(); write_fs_48(key); unsigned long stored_key = read_fs_48(); if (stored_key != key) { exit (-1 ); } initialized = 1 ; } return read_fs_48(); } __attribute__((destructor)) void restore_fs_original_value () { if (read_fs_48()) { write_fs_48(0 ); if (shadow_stack != NULL ) { munmap(shadow_stack, 4096 ); shadow_stack = NULL ; } } } __attribute__((constructor)) void shadow_stack_init () { if (read_fs_48()) return ; generate_and_store_shadow_key(); shadow_stack = mmap(NULL , 4096 , PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1 , 0 ); if (shadow_stack == MAP_FAILED) { safe_write("[system]Shadow stack init failed\n" ); exit (1 ); } sbp = (void **)((char *)shadow_stack + 4096 ); ssp = sbp; }void __fshadow_stack_push_encrypted(void *ret_addr) { if (!read_fs_48() || shadow_stack == NULL ) return ; if (ssp <= (void **)shadow_stack) { safe_write("[system]Shadow stack overflow!\n" ); exit (1 ); } void *encrypted_addr = (void *)((unsigned long )ret_addr ^ read_fs_48()); ssp--; *ssp = encrypted_addr; }void __fshadow_stack_check_encrypted(void *curr_addr) { if (!read_fs_48() || shadow_stack == NULL ) return ; if (ssp >= sbp) { return ; } void *encrypted_addr = *ssp; void *decrypted_addr = (void *)((unsigned long )encrypted_addr ^ read_fs_48()); ssp++; if (curr_addr != decrypted_addr) { safe_write("\n=== FS:0x48 SHADOW STACK SECURITY ALERT ===\n" ); safe_write("Return address tampering detected!\n" ); safe_write("FS:0x48-based encryption detected attack!\n" ); safe_write("Possible ROP/JOP attack blocked!\n" ); safe_write("Process terminated for security.\n" ); safe_write("==========================================\n" ); _exit(1 ); } }#define PROTECTED_FUNC_BEGIN() \ do \ { \ void *__ret_addr = __builtin_return_address(0); \ __fshadow_stack_push_encrypted(__ret_addr); \ } while (0) #define PROTECTED_FUNC_END() \ do \ { \ void *__ret_addr = __builtin_return_address(0); \ __fshadow_stack_check_encrypted(__ret_addr); \ } while (0) void vulnerable_function () { PROTECTED_FUNC_BEGIN(); char buffer[16 ]; printf ("输入数据: " ); gets(buffer); printf ("您输入的是: %s\n" , buffer); PROTECTED_FUNC_END(); }int main () { PROTECTED_FUNC_BEGIN(); vulnerable_function(); PROTECTED_FUNC_END(); return 0 ; }
脚本 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 #!/bin/bash SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]} " ) " && pwd) " SHADOW_RUNTIME_DIR="$SCRIPT_DIR " ORIGINAL_GCC="gcc" RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' show_help () { echo "Shadow Stack Compiler Wrapper" echo "用法: $0 [选项] 源文件..." echo "" echo "Shadow Stack 选项:" echo " -shadow 启用影子栈保护" echo " -shadow-debug 启用影子栈保护并显示调试信息" echo " -shadow-silent 启用影子栈保护但不显示系统信息" echo " -shadow-help 显示此帮助信息" echo "" echo "其他选项将直接传递给 GCC" echo "" echo "示例:" echo " $0 -shadow test.c -o test" echo " $0 -shadow-debug -g test.c -o test" echo " $0 -shadow-silent test.c -o test" echo "" echo "注意:" echo " - 必须包含至少一个 .c/.cpp/.cc/.cxx 源文件" echo " - 使用影子栈选项时,源文件会被自动处理" echo " - 不使用影子栈选项时,直接调用原始 GCC" }show_error () { echo -e "${RED} 错误: $1${NC} " >&2 echo "" show_help exit 1 } ENABLE_SHADOW=0 SHADOW_DEBUG=0 SHADOW_SILENT=0 GCC_ARGS=() SOURCE_FILES=() UNKNOWN_SHADOW_OPTIONS=()if [[ $# -eq 0 ]]; then show_help exit 0fi while [[ $# -gt 0 ]]; do case $1 in -shadow-help) show_help exit 0 ;; -shadow-debug) ENABLE_SHADOW=1 SHADOW_DEBUG=1 shift ;; -shadow-silent) ENABLE_SHADOW=1 SHADOW_SILENT=1 shift ;; -shadow) ENABLE_SHADOW=1 shift ;; -shadow-*) UNKNOWN_SHADOW_OPTIONS+=("$1 " ) shift ;; *.c|*.cpp|*.cc|*.cxx) SOURCE_FILES+=("$1 " ) GCC_ARGS+=("$1 " ) shift ;; *) GCC_ARGS+=("$1 " ) shift ;; esac done if [[ ${#UNKNOWN_SHADOW_OPTIONS[@]} -gt 0 ]]; then show_error "未知的影子栈选项: ${UNKNOWN_SHADOW_OPTIONS[*]} " fi if [[ $ENABLE_SHADOW -eq 1 ]]; then if [[ ${#SOURCE_FILES[@]} -eq 0 ]]; then show_error "启用影子栈保护时必须指定至少一个源文件 (.c/.cpp/.cc/.cxx)" fi echo -e "${GREEN} [Shadow Stack]${NC} 启用影子栈保护" TEMP_DIR=$(mktemp -d) trap "rm -rf $TEMP_DIR " EXIT MODIFIED_FILES=() for src_file in "${SOURCE_FILES[@]} " ; do if [[ -f "$src_file " ]]; then base_name=$(basename "$src_file " ) temp_file="$TEMP_DIR /$base_name " echo -e "${BLUE} [Shadow Stack]${NC} 处理源文件: $src_file " { head -n 10 "$src_file " | grep -E '^#include' cat << 'EOF' // ==================== Shadow Stack Runtime ==================== char *gets(char *s); // Shadow stack runtime void *shadow_stack; void **ssp; void **sbp; int shadow_stack_enabled = 0; static int in_hook = 0; static size_t safe_strlen(const char *s) { size_t len = 0; while (s[len]) len++; return len; } static void safe_write(const char *msg) { write(STDERR_FILENO, msg, safe_strlen(msg)); } static inline void write_in(unsigned long value) { __asm__ volatile("movq %0, %%fs:0x48" : : "r" (value)); } static inline unsigned long read_in () { unsigned long key; __asm__ volatile("movq %%fs:0x48, %0" : "=r" (key)); return key; } unsigned long read_urandom_key () { int fd; unsigned long key = 0; ssize_t bytes_read; fd = open("/dev/urandom" , O_RDONLY); if (fd == -1) { safe_write("[ERROR] 无法打开/dev/urandom\n" ); exit (-1); } bytes_read = read (fd, &key, sizeof(key)); if (bytes_read != sizeof(key)) { safe_write("[WARNING] key读取出错\n" ); exit (-1); } close(fd); return key; } unsigned long generate_and_store_shadow_key () { static int initialized = 0; if (!initialized) { unsigned long key = read_urandom_key(); write_in(key); unsigned long stored_key = read_in(); if (stored_key != key) { safe_write("[ERROR] 密钥验证失败\n" ); exit (-1); } initialized = 1; //safe_write("[system] 加密密钥已生成\n" ); } return read_in(); } __attribute__((constructor)) void shadow_stack_init () { if (shadow_stack_enabled) return ; generate_and_store_shadow_key(); shadow_stack = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (shadow_stack == MAP_FAILED) { safe_write("[system] Shadow stack init failed\n" ); exit (1); } sbp = (void **)((char *)shadow_stack + 4096 ); ssp = sbp; shadow_stack_enabled = 1 ; #ifndef SHADOW_SILENT safe_write("[system] Shadow stack protection was enabled \n"); #endif } __attribute__((destructor)) void shadow_stack_cleanup () { if (shadow_stack_enabled) { shadow_stack_enabled = 0; if (read_in()) { write_in(0); } if (shadow_stack != NULL) { munmap(shadow_stack, 4096); shadow_stack = NULL; } } } void __fshadow_stack_push_encrypted(void *ret_addr) { if (shadow_stack == NULL || !read_in()) return ; if (ssp <= (void **)shadow_stack) { safe_write("[system] Shadow stack overflow!\n" ); exit (1); } void *encrypted_addr = (void *)((unsigned long)ret_addr ^ read_in()); ssp--; *ssp = encrypted_addr; safe_write("[DEBUG] 加密返回地址并推入影子栈\n" ); } void __fshadow_stack_check_encrypted(void *curr_addr) { if (shadow_stack == NULL || !read_in()) return ; if (ssp >= sbp) { return ; } void *encrypted_addr = *ssp; void *decrypted_addr = (void *)((unsigned long)encrypted_addr ^ read_in()); ssp++; safe_write("[DEBUG] 解密并检查返回地址\n" ); if (curr_addr != decrypted_addr) { safe_write("\n=== SHADOW STACK SECURITY ALERT ===\n" ); safe_write("Return address tampering detected!\n" ); safe_write("Simulate shadow stack detected attack!\n" ); safe_write("Possible ROP/JOP attack blocked!\n" ); safe_write("Process terminated for security.\n" ); safe_write("==========================================\n" ); _exit(1); } } void __fshadow_stack_push(void *ret_addr) { __fshadow_stack_push_encrypted(ret_addr); } void __fshadow_stack_check(void *curr_addr) { __fshadow_stack_check_encrypted(curr_addr); } __attribute__((no_instrument_function)) void __cyg_profile_func_enter(void *this_fn, void *call_site) { if (!shadow_stack_enabled || in_hook) return ; in_hook = 1; if (call_site != NULL && call_site != (void *)0x1) { safe_write("[DEBUG] Function enter - pushing encrypted address to shadow stack\n" ); } else { safe_write("[DEBUG] Function enter - skipping (invalid call_site)\n" ); } if (call_site != NULL && call_site != (void *)0x1 && shadow_stack != NULL) { __fshadow_stack_push_encrypted(call_site); } in_hook = 0; } __attribute__((no_instrument_function)) void __cyg_profile_func_exit(void *this_fn, void *call_site) { if (!shadow_stack_enabled || in_hook) return ; in_hook = 1; // 设置标志,防止递归 if (call_site != NULL && call_site != (void *)0x1) { safe_write("[DEBUG] Function exit - checking encrypted shadow stack\n" ); } else { safe_write("[DEBUG] Function exit - skipping (invalid call_site)\n" ); } if (call_site != NULL && call_site != (void *)0x1 && shadow_stack != NULL) { __fshadow_stack_check_encrypted(call_site); } in_hook = 0; } // ==================== End Shadow Stack Runtime ==================== EOF tail -n +1 "$src_file " | grep -v '^#include' } > "$temp_file " MODIFIED_FILES+=("$temp_file " ) fi done NEW_GCC_ARGS=() for arg in "${GCC_ARGS[@]} " ; do found=0 for i in "${!SOURCE_FILES[@]} " ; do if [[ "$arg " == "${SOURCE_FILES[$i]} " ]]; then NEW_GCC_ARGS+=("${MODIFIED_FILES[$i]} " ) found=1 break fi done if [[ $found -eq 0 ]]; then NEW_GCC_ARGS+=("$arg " ) fi done SHADOW_FLAGS=( "-finstrument-functions" ) if [[ $SHADOW_DEBUG -eq 1 ]]; then SHADOW_FLAGS+=("-DSHADOW_DEBUG" ) echo -e "${YELLOW} [Shadow Stack]${NC} 启用调试模式" fi if [[ $SHADOW_SILENT -eq 1 ]]; then SHADOW_FLAGS+=("-DSHADOW_SILENT" ) echo -e "${YELLOW} [Shadow Stack]${NC} 启用静默模式" fi FINAL_COMMAND=( "$ORIGINAL_GCC " "${SHADOW_FLAGS[@]} " "${NEW_GCC_ARGS[@]} " ) echo -e "${BLUE} [Shadow Stack]${NC} 执行编译命令:" echo -e "${BLUE} [Shadow Stack]${NC} ${FINAL_COMMAND[*]} " "${FINAL_COMMAND[@]} " compile_result=$? if [[ $compile_result -eq 0 ]]; then echo -e "${GREEN} [Shadow Stack]${NC} 编译成功,影子栈保护已启用" else echo -e "${RED} [Shadow Stack]${NC} 编译失败" fi exit $compile_result else exec "$ORIGINAL_GCC " "${GCC_ARGS[@]} " fi