{ "topic": "buffer-overflow", "type": "true_false", "schema_version": "1.0.0", "generated": "2026-09-17T00:00:00+08:00", "questions": [ { "id": "tf-001", "type": "true_false", "difficulty": 1, "tags": [ "缓冲区溢出" ], "question": "C 语言中的 strcpy() 函数会自动检查目标缓冲区的大小,防止溢出。", "answer": false, "explanation": "strcpy() 不会检查目标缓冲区的大小,它只是简单地将源字符串复制到目标地址直到遇到 '\\0' 终止符。这是它成为缓冲区溢出经典案例的根本原因。要安全地复制字符串,应使用 strncpy()、strlcpy() 或 strcpy_s() 等带长度限制的替代函数。", "source": null, "related": [] }, { "id": "tf-002", "type": "true_false", "difficulty": 2, "tags": [ "dep", "漏洞利用" ], "question": "DEP(数据执行保护)可以完全阻止所有类型的缓冲区溢出攻击。", "answer": false, "explanation": "DEP 只能阻止在数据区域(如栈和堆)直接执行注入代码的攻击方式。但它无法阻止 ROP(Return-Oriented Programming)等不注入新代码的攻击技术,因为 ROP 利用的是已有可执行代码段中的 gadget。此外,JIT-Spray 等技术也可以绕过 DEP。因此 DEP 是重要的安全层,但并非万能。", "source": null, "related": [] }, { "id": "tf-003", "type": "true_false", "difficulty": 2, "tags": [ "aslr", "漏洞利用" ], "question": "当系统启用了 ASLR 后,攻击者就无法预测任何内存地址,因此缓冲区溢出攻击不可能成功。", "answer": false, "explanation": "ASLR 虽然随机化了内存布局,但可以通过信息泄露漏洞(如格式化字符串漏洞、越界读取等)获取已知模块的地址,从而计算偏移量绕过 ASLR。此外,部分系统中 ASLR 的熵可能不足(如 32 位系统的随机化空间有限),暴力破解也是可行的。还存在部分覆盖(partial overwrite)等技术。因此 ASLR 大大提高了攻击难度,但并非不可绕过。", "source": null, "related": [] }, { "id": "tf-004", "type": "true_false", "difficulty": 1, "tags": [ "栈溢出" ], "question": "栈的增长方向是从高地址向低地址,而栈上缓冲区的写入方向通常是从低地址向高地址。", "answer": true, "explanation": "在 x86/x64 架构中,栈指针(ESP/RSP)在 push 操作时递减(向低地址增长),这是栈的生长方向。而数组/缓冲区的元素按索引递增方向存储(从低地址到高地址)。正是这两个方向的不一致,使得缓冲区溢出可以向高地址方向覆盖保存的 EBP 和返回地址,从而实现栈溢出攻击。", "source": null, "related": [] }, { "id": "tf-005", "type": "true_false", "difficulty": 2, "tags": [ "rop", "漏洞利用" ], "question": "ROP 攻击需要注入新的可执行代码到目标进程中。", "answer": false, "explanation": "ROP(Return-Oriented Programming)的核心特点恰恰是不注入任何新代码。它完全利用目标程序或其加载的共享库中已存在的代码片段(gadget),通过在栈上精心布局 gadget 地址,利用 ret 指令串联执行。正是因为不注入新代码,ROP 可以绕过将数据区域标记为不可执行的 DEP 保护。", "source": null, "related": [] }, { "id": "tf-006", "type": "true_false", "difficulty": 1, "tags": [ "缓冲区溢出" ], "question": "gets() 函数已被 C11 标准正式废弃,不推荐在任何场景中使用。", "answer": true, "explanation": "gets() 函数由于天生不安全(没有任何方式限制读取长度),已被 C11 标准正式移除。POSIX 标准也已将其标记为废弃。如果程序中仍在使用 gets(),应替换为 fgets() 或其他带长度限制的输入函数。几乎所有现代编译器在检测到 gets() 使用时都会发出警告。", "source": null, "related": [] }, { "id": "tf-007", "type": "true_false", "difficulty": 2, "tags": [ "栈溢出" ], "question": "Stack Canary 的值在每次进程运行时都是相同的,但每次函数调用时会变化。", "answer": false, "explanation": "在现代实现中,Stack Canary 的值在进程启动时随机生成(通过 /dev/urandom 等),每次进程运行时值都不同。在进程生命周期内,同一函数的 canary 值通常保持不变(进程级随机化)。有些实现可能在 fork 时刷新 canary,但典型的用户态程序中 canary 在进程级别是固定的,而非每次函数调用都变化。关键在于每次进程运行时的值不同,使得攻击者无法预先知道 canary。", "source": null, "related": [] }, { "id": "tf-008", "type": "true_false", "difficulty": 1, "tags": [ "缓冲区溢出" ], "question": "缓冲区溢出漏洞只存在于 C/C++ 等不提供自动内存管理的语言中。", "answer": true, "explanation": "缓冲区溢出的根源是缺乏自动边界检查和内存安全保护。C/C++ 直接操作内存,不提供数组边界检查,是缓冲区溢出的高发语言。Java、Python 等语言提供自动内存管理和边界检查,不会产生经典的缓冲区溢出。但需注意:这些语言的底层解释器/虚拟机如果是用 C/C++ 实现的,其自身可能存在缓冲区溢出漏洞。从语言特性层面来说,该说法基本正确。", "source": null, "related": [] }, { "id": "tf-009", "type": "true_false", "difficulty": 2, "tags": [ "aslr", "漏洞利用" ], "question": "在 Linux 系统中,非 PIE(Position Independent Executable)的可执行文件加载地址是固定的,其代码段地址不受 ASLR 影响。", "answer": true, "explanation": "在 Linux 中,只有位置无关可执行文件(PIE,使用 -fPIE -pie 编译)的加载地址才会被 ASLR 随机化。非 PIE 可执行文件的代码段(.text)加载到固定的基地址(如 0x400000),不受 ASLR 影响。但其栈、堆、共享库(libc 等)地址仍然会被随机化。因此开启 PIE(编译选项 -pie)是提高 ASLR 有效性的重要措施。", "source": null, "related": [] }, { "id": "tf-010", "type": "true_false", "difficulty": 1, "tags": [ "缓冲区溢出" ], "question": "地址无关代码(PIC / PIE)是一种缓解缓冲区溢出利用的防御机制。", "answer": true, "explanation": "PIE(Position Independent Executable)使可执行文件的代码段在每次加载时地址随机化,配合 ASLR 使得攻击者无法预测代码段地址,从而更难构造 ROP 链或预测 gadget 位置。虽然 PIE 本身不阻止缓冲区溢出的发生,但它是重要的利用缓解措施,提高了攻击者利用溢出漏洞的难度。这是纵深防御策略的一部分。", "source": null, "related": [] } ] }