记录一只金丝雀

记录一只金丝雀
金丝雀案例代码#include stdio.h #include string.h void vulnerable_function(char *input) { char buffer[8]; // 在栈上分配64字节的局部数组 strcpy(buffer, input); // 危险操作未检查长度可能溢出 printf(Input: %s\n, buffer); } int main(int argc, char **argv) { if (argc 2) return 0; vulnerable_function(argv[1]); // 从命令行接收输入 return 0; }编译g a.cpp -fstack-protector这是没有金丝雀的样子 上面的buffer长度改成70000000000001189 _Z19vulnerable_functionPc: 1189: f3 0f 1e fa endbr64 118d: 55 push %rbp 118e: 48 89 e5 mov %rsp,%rbp 1191: 48 83 ec 20 sub $0x20,%rsp 1195: 48 89 7d e8 mov %rdi,-0x18(%rbp) 1199: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax 11a0: 00 00 11a2: 48 89 45 f8 mov %rax,-0x8(%rbp) 11a6: 31 c0 xor %eax,%eax 11a8: 48 8b 55 e8 mov -0x18(%rbp),%rdx 11ac: 48 8d 45 f0 lea -0x10(%rbp),%rax 11b0: 48 89 d6 mov %rdx,%rsi 11b3: 48 89 c7 mov %rax,%rdi 11b6: e8 b5 fe ff ff call 1070 strcpyplt 11bb: 48 8d 45 f0 lea -0x10(%rbp),%rax 11bf: 48 89 c6 mov %rax,%rsi 11c2: 48 8d 05 3b 0e 00 00 lea 0xe3b(%rip),%rax # 2004 _IO_stdin_used0x4 11c9: 48 89 c7 mov %rax,%rdi 11cc: b8 00 00 00 00 mov $0x0,%eax 11d1: e8 ba fe ff ff call 1090 printfplt 11d6: 90 nop 11d7: 48 8b 45 f8 mov -0x8(%rbp),%rax 11db: 64 48 2b 04 25 28 00 sub %fs:0x28,%rax 11e2: 00 00 11e4: 74 05 je 11eb _Z19vulnerable_functionPc0x62 11e6: e8 95 fe ff ff call 1080 __stack_chk_failplt 11eb: c9 leave 11ec: c3 ret这是带金丝雀的样子0000000000001189 _Z19vulnerable_functionPc: 1189: f3 0f 1e fa endbr64 118d: 55 push %rbp 118e: 48 89 e5 mov %rsp,%rbp 1191: 48 83 ec 20 sub $0x20,%rsp 1195: 48 89 7d e8 mov %rdi,-0x18(%rbp) 1199: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax 11a0: 00 00 11a2: 48 89 45 f8 mov %rax,-0x8(%rbp) 11a6: 31 c0 xor %eax,%eax 11a8: 48 8b 55 e8 mov -0x18(%rbp),%rdx 11ac: 48 8d 45 f0 lea -0x10(%rbp),%rax 11b0: 48 89 d6 mov %rdx,%rsi 11b3: 48 89 c7 mov %rax,%rdi 11b6: e8 b5 fe ff ff call 1070 strcpyplt 11bb: 48 8d 45 f0 lea -0x10(%rbp),%rax 11bf: 48 89 c6 mov %rax,%rsi 11c2: 48 8d 05 3b 0e 00 00 lea 0xe3b(%rip),%rax # 2004 _IO_stdin_used0x4 11c9: 48 89 c7 mov %rax,%rdi 11cc: b8 00 00 00 00 mov $0x0,%eax 11d1: e8 ba fe ff ff call 1090 printfplt 11d6: 90 nop 11d7: 48 8b 45 f8 mov -0x8(%rbp),%rax 11db: 64 48 2b 04 25 28 00 sub %fs:0x28,%rax 11e2: 00 00 11e4: 74 05 je 11eb _Z19vulnerable_functionPc0x62 11e6: e8 95 fe ff ff call 1080 __stack_chk_failplt 11eb: c9 leave 11ec: c3 ret执行效果ubuntu22NYX:~/ardu/ardupilot_clean$ ./a.out 123123123123123Input: 123123123123123*** stack smashing detected ***: terminatedAborted (core dumped)代码分步解析获取金丝雀值 (第4-5步)assembly1199: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax 11a0: 00 00 11a2: 48 89 45 f8 mov %rax,-0x8(%rbp)mov %fs:0x28,%rax从线程局部存储%fs:0x28中读取一个随机值这就是金丝雀。mov %rax,-0x8(%rbp)将这个值保存在栈帧中-0x8(%rbp)的位置。初始化局部变量第6步assembly11a6: 31 c0 xor %eax,%eax这里将%eax清零这是一个常见的操作用于消除金丝雀值在寄存器中的痕迹提高一点安全性。函数核心功能第7-13步assembly11a8: 48 8b 55 e8 mov -0x18(%rbp),%rdx 11ac: 48 8d 45 f0 lea -0x10(%rbp),%rax ... 11bb: 48 8d 45 f0 lea -0x10(%rbp),%rax ... 11d6: 90 nop这部分就是strcpy和printf的调用处理你的buffer数组位于-0x10(%rbp)和输入字符串。检查金丝雀第15-17步assembly11d7: 48 8b 45 f8 mov -0x8(%rbp),%rax 11db: 64 48 2b 04 25 28 00 sub %fs:0x28,%rax 11e2: 00 00 11e4: 74 05 je 11eb ... 11e6: e8 95 fe ff ff call 1080 __stack_chk_failpltmov -0x8(%rbp),%rax从栈上读取之前保存的金丝雀值。sub %fs:0x28,%rax用栈上的值减去原始值。如果两者相同结果为0je跳转生效函数正常返回。如果结果不为0则调用__stack_chk_fail触发栈溢出保护。 内存布局与阈值验证从sub $0x20,%rsp可以算出该函数在栈上分配了32 字节0x20。结合变量的位置位置内容说明-0x8(%rbp)栈金丝雀 (8 字节)紧邻rbp处于关键位置-0x10(%rbp)buffer数组 (16 字节)这就char buffer[64]-0x18(%rbp)保存的参数指针存储input指针这表明使用的char buffer[64]明确触发了金丝雀保护。阈值规则生效因为数组长度 (64) 大于 8 字节编译器判定存在风险所以为该函数插入了完整的金丝雀机制。