面灵AI→

360 C++ 一面:安全研发视角下的内存破坏、句柄与 PE 加载

轮次
一面
时间
2026-10
来源
牛客网

《面试题目》

  1. 安全研发和普通开发有什么不同?你了解 360 的业务吗?
  2. 缓冲区溢出是怎么发生的?现代防护有哪些?
  3. UAF(Use-After-Free)为什么危险?怎么防?
  4. Windows 句柄和 Linux fd 有什么可对比的?泄漏了怎么查?
  5. DLL 注入是什么思路?有哪些合法用途?
  6. PE 文件结构了解吗?从一个文件到执行,加载过程发生了什么?
  7. 反调试和反钩子是什么思路?安全软件为什么要这么干?

《参考解析》

安全研发和普通开发的差别:普通开发以功能正确为目标,输入按「正常用户会怎么用」来设计;安全研发默认假设所有输入都是恶意的,先划信任边界——哪些数据不可信、在哪一层校验、越界之后影响面多大,再按攻击面拆解析器、协议实现、文件格式和序列化。能力上更依赖底层功底,对内存布局、调用约定、汇编的理解是地基。360 的业务面是终端安全、浏览器、政企安全,答业务认知时能对一个具体产品说出技术判断,比泛泛而谈有效。

缓冲区溢出与防护链:成因是向定长缓冲区写入超长数据、覆盖相邻内存,栈上溢出能改掉返回地址直接劫持控制流。防护是一层层加的:栈金丝雀在返回地址前放哨兵,返回时校验;DEP/NX 让数据页不可执行,挡住注入的 shellcode;ASLR 随机化模块基址与栈堆地址,抬高定位 gadget 的成本;SafeSEH/CFG 校验异常处理链与间接调用目标;编译器侧还有 fortify 与 strong stack protector。绕过与对抗是循环:ROP 绕 NX,信息泄露破 ASLR。答题讲清每层防的是什么,比罗列名词有用。

UAF 为什么危险:对象已经释放但指针还在,这块内存被回收后可能被别的数据占住。攻击者用堆喷填成同样大小的伪对象(关键是伪造虚表指针),一次野指针调用就变成对可控地址的调用。防御分三层:生命周期上尽量用智能指针与引用计数、释放即置空;分配器上用隔离堆按大小或类型分区,降低占位成功率;运行时用 ASan 在测试阶段抓,线上靠 CFI 限制间接调用目标。工程纪律是所有权清晰,「析构后仍对外暴露的句柄」是高危模式。

句柄与 fd 的对比和泄漏排查:两者本质都是内核对象的索引。Windows 的句柄存在进程句柄表里,带类型与访问权限;Linux 的 fd 是进程 fd 表的下标,指向打开的文件描述。泄漏的表现都是只涨不减:Windows 用 Process Explorer 看句柄总数与类型分布,或 GetProcessHandleCount 打点;Linux 直接数 /proc//fd 的条目并看指向。最常见的原因是失败分支漏关——出错路径直接 return,成功路径关了、失败路径忘了。根治靠 RAII 包装加静态检查,光靠 review 盯不住。

DLL 注入与 PE 结构:注入的本质是让目标进程加载你控制的代码,常见手法有远程线程调 LoadLibrary、全局钩子、导入表或 APC 劫持。它有大量正当用途:输入法、无障碍、性能分析器、游戏 mod、安全软件的主动防御 hook;同一套技术既被外挂木马用,也是很多正常工具的原理。PE 从 DOS 头(e_lfanew 指向签名)→ 签名 → 文件头 → 可选头(入口点、镜像基址、数据目录)→ 节表(.text/.data/.rdata/.reloc)。加载时按节映射、按 ASLR 基址重定位、解析导入表填 IAT、跑 TLS 回调、跳到入口点。这是逆向和壳分析的基本功。

反调试与反钩子:反调试是检测调试器留下的痕迹——PEB 的 BeingDebugged、IsDebuggerPresent、硬件断点寄存器、代码段里的 int3、时间差检测,目的是拖慢动态分析。反钩子是另一侧:安全软件 hook 关键 API 做拦截与行为监控,恶意样本则用直接系统调用、恢复被改的字节、扫描 hook 跳转来绕过。本质是「谁控制执行路径」的对抗。