
360公司2014校招笔试卷1. 为什么安全公司的笔试卷比技术博客更值得复盘2014年对国内互联网公司来说是一个很微妙的时间节点。移动互联网刚把所有赛道重新洗牌安卓/iOS开发的校招岗位一抓一大把而传统意义上的安全公司在学生群体里反而显得有点冷门。360那年的校招笔试延续了它一贯的实用主义风格——不考八股文式的背诵不考让人摸不着头脑的智力题整张卷子围绕C/C、数据结构、操作系统、网络协议和安全基础展开题目量不大但每一道都能看出来出题人想筛出什么样的人。我当时是抱着试试水的心态投的这个岗位。笔试前我刷了不少普通互联网公司的真题大多集中在Java语法的边角料、数据库范式、HTTP状态码这类面经高频题上。真正坐到360的笔试卷面前我才意识到一个核心差异普通公司考你会不会用技术安全公司考你知不知道技术为什么脆弱、边界在哪里。整套卷子没有一个编程大题是让你默写快排或者手撕单例的反而是大量C语言指针、内存布局、字符串处理相关的题目——这些恰恰是日常开发里最容易写错、也最容易被漏洞利用者盯上的地方。如果你是准备安全方向或底层开发方向的校招生这份卷子比任何一本《程序员面试宝典》都值得反复复盘。它不只是考察知识点更像是在模拟一个场景给你一段接近真实生产的代码你能不能在写之前就预判出它会在哪里炸掉。这种思维模式刷题刷不出来只能靠大量阅读源码和亲手踩坑积累。现在回看这份卷子的题目结构可以拆成几大块考察模块典型题型考察目标C/C基础指针数组、数组指针、sizeof、内存对齐底层基本功是否扎实数据结构与算法链表反转、二分查找、二叉树遍历、DP解题思路与代码实现能力操作系统进程线程、死锁、内存管理系统级思维网络协议TCP握手、HTTP协议细节协议理解深度安全特色缓冲区溢出、格式化字符串、整数溢出安全敏感度下面我按模块把当时印象最深、也最有代表性的题目逐类拆开每道题都附上解题思路和出题意图分析。这些都是我当年考场上的真实记忆结合后续工作的理解反推出来的不保证和原卷一字不差但考察逻辑和知识核心是完全一致的。2. C/C与内存安全指针题目背后的安全思维2.1 sizeof一个指针为什么会考倒一片人360的卷子里有一道非常经典的sizeof题目在32位和64位环境下用C语言定义一个char *p (char *)malloc(100);问你sizeof(p)、sizeof(*p)、strlen(p)分别是多少。普通公司也爱考这道题但360的考法更刁钻一些——它还会追问一句如果在malloc之前就调用strlen(p)会发生什么先拆解基础答案。sizeof(p)是指针变量本身占用的内存大小和你malloc了100字节还是1000字节没有任何关系只跟编译平台有关32位下是464位下是8。sizeof(*p)是p所指向的单个元素类型的大小char类型恒为1不管什么平台。strlen(p)则是函数它沿着p指向的地址往下逐字节扫描直到遇到\0为止。malloc返回的内存是未初始化状态里面可能是上一位程序留下的残骸也可能全是0所以strlen的结果是一个完全不确定的值。这道题的进阶考点在于strlen和sizeof的一个求值依赖运行时状态一个求值依赖编译期类型这是两种完全不同的抽象层次。把这两个东西混为一谈在笔试里顶多丢几分但在真实的安全代码里就是信息泄露的入口。我在之后做代码审计时见到过一个真实案例开发者用strlen的结果来给协议头里的长度字段赋值但协议头本身要求的是缓冲区容量而不是字符串实际长度结果导致对端读取时越界直接可以被打造成一个堆越界读漏洞。这道题还牵扯出一个关键的内存敏感点——malloc和calloc的取舍。malloc不清理内存calloc会先把分配的内存清零。笔试后的面试环节面试官通常还会追问为什么安全敏感的代码里更应该用calloc 因为未初始化的内存可能残留密钥、敏感字符串、堆地址等数据一旦被日志打印、被给到客户端、被拼接进错误消息就属于典型的信息泄露。2014年曝光的很多Web应用漏洞追到根上都是这类小细节。提示sizeof是编译期运算符strlen是运行时函数。写安全代码时凡是涉及长度计算先想清楚你需要的是编译器告诉你它有多大还是运行时扫描到结束符才知道它有多长。滥用strlen处理非字符串二进制缓冲区是C语言程序最常见的越界读入口之一。2.2 数组名和指针的关系远不是等价的这么简单还有一道题现在看依然是经典问的是char a[] hello;和char *p hello;在某些操作上的区别。比如sizeof(a)和sizeof(p)分别是多少a是否合法p是否合法以及修改a[0]和修改p[0]分别会发生什么。先说基础答案。sizeof(a)是6因为字符串含有5个字符加末尾的\0sizeof(p)是4或8取决于平台。a是数组名代表整个数组的首地址不是一个独立的指针变量所以a不合法编译器直接报错p是一个真正的指针变量指向字符串字面量所在的内存区域p完全合法。最后一点是杀招——a[0] H没问题字符串存在栈上可以修改p[0] H大概率直接段错误因为字符串字面量通常存放在只读数据段修改它就是在向只读内存写入。这道题在安全圈的意义不亚于strcpy的溢出问题。2014年前后我接触过的好多CVE根因都是开发者把指针和数组混为一谈默认指针指向的那块内存一定是可写的。比如某些老牌IM软件在处理历史消息时直接用指针指向了一个协议包里的字符串字段然后尝试原地转大写、去空格结果在特定硬件平台和编译器组合下直接崩溃——因为协议包的缓冲区在只读段。另外这道题还隐含了一个C语言新手非常容易踩的坑函数传参时数组会退化为指针。不管你函数签名写的是void foo(char a[100])还是void foo(char a[])在函数内部sizeof(a)得到的都只是指针大小而不是100。很多人在笔试加班题里写冒泡排序/字符串反转传入数组后顺手sizeof(a)/sizeof(a[0])计算长度结果分段错误或排序个数错误。360的卷子没有专门出这个但面试环节经常以此为基础聊到C语言的数组退化机制。这背后隐藏的安全含义是一旦长度信息在传参过程中丢失函数内部就无法校验边界缓冲区溢出风险随之而来。2.3 内存对齐与union的内存布局笔试罕见但安全重要的考点那份卷子里有一道结构体大小计算题考的是内存对齐规则和union的内存布局。题目类似这样typedef struct { char a; // 1字节 int b; // 4字节 char c; // 1字节 } AlignStruct; typedef union { char chars[8]; struct { int low; int high; } ints; } DataUnion;问sizeof(AlignStruct)和sizeof(DataUnion)各是多少以及某个小的结构体被用memcpy逐字节拷贝到网络字节序缓冲区时填充字节里会不会残留栈上旧数据。AlignStruct的答案在32位和64位默认对齐规则下都是12而不是简单的1416。因为int的对齐要求是4字节char a后面要填充3个字节char c后面为了让结构体整体大小是最大对齐数的整数倍又要填充3个字节。DataUnion在32位和64位下都是8因为union的大小取其最大成员的大小。这道题真正让人冒冷汗的是延伸问题结构体填充字节里的内容是未定义的它们可能是栈上之前遗留的老数据。如果你把这个结构体直接memcpy到一个发送缓冲区然后通过socket发出去填充字节里的残留数据就被一起带出去了。这属于信息泄露的一种隐蔽形式2000年初至2014年间有不少协议解析类漏洞就是靠这类填充字节泄露栈指针、堆地址等敏感信息来完成的。处理办法也很简单发送前用memset把整个结构体清零或者逐字段序列化到缓冲区而不是整块内存直接拷贝。这个考点在普通开发岗笔试卷里很少出现但安全公司把它放进来考察的就是工程师有没有内存比你想象的多出来一块的意识。安全的本质不外乎内存里不该被看到的数据不允许出现在任何能被外部读取的地方。3. 数据结构与算法链表的递归、排序的边界3.1 链表反转的三种写法以及一种教科书没写的链表反转是笔试的常客360的卷子上它出现了两次一次是单独的编程题一次是在选择题里考递归反转时的栈溢出风险。编程题的完整描述大概是给定一个单链表请实现函数将其反转返回新头结点。要求不能使用额外的O(n)空间不能修改节点的值。 限制条件很关键——不能修改节点值意味着你不能遍历一遍把值取出来再倒着填回去必须真的去动指针。标准答案有三种迭代反转、递归反转、头插法。迭代反转是最推荐的写法时间O(n)、空间O(1)用三个指针各自记录前驱、当前、后继逐个翻转next方向考查的是最基础的指针操作几乎不允许出错typedef struct Node { int val; struct Node *next; } Node; Node *reverse_iterative(Node *head) { Node *prev NULL; Node *curr head; while (curr) { Node *next curr-next; curr-next prev; prev curr; curr next; } return prev; }递归反转代码极短初看很优雅但360的笔试后面试官一定会引导你思考链表有10000个节点时递归深度就是10000层默认栈空间Linux通常8MB可能被撑爆导致段错误。如果你能主动提到递归版本在大链表下有栈溢出风险所以生产环境我更倾向迭代版本——这句话在面试官那里的加分远超过写出正确答案本身。在安全公司这尤其重要栈溢出不只是笔试概念缓冲区溢出和栈溢出在CVE里经常就是递归深度不可控导致的。攻击者通过精心构造的数据让递归函数不断加深调用栈最终覆盖到不可预期的内存区域就是哈希表碰撞攻击、JSON解析栈溢出攻击的典型套路。第三种教科书没写的方式其实是用一个栈来实现反转遍历链表时把节点指针逐个压栈遍历结束后依次弹栈并重新串联。这种写法用了O(n)的辅助空间普通笔试里会被扣分但它在真实场景中有特殊价值——它把指针的指针这种容易出错的密集操作转换成了程序员更熟悉的数组/栈操作。我在实际工作中维护过一段不得不写链表反转的代码接手的人里十个有九个看不懂原版三指针迭代法改用栈法之后可读性明显提高。笔试需要给出最优解但你要让考官知道你知道这个折中方案存在的合理性。3.2 二分查找的边界一个循环就让你心态崩掉算法题里还有一道二分查找在一个有序数组里查找目标值返回索引如果不存在返回-1。看起来简单到不配出现在笔试里但360把条件稍作修改数组里可能存在重复元素要求返回第一个等于目标值的索引。就这一个条件变化能把很多人打回原型。经典写法里mid (low high) / 2没问题但放到边界条件里可能会出现死循环。要找第一个等于目标值的元素必须保证当nums[mid]大于等于目标值时high向左收缩当nums[mid]小于目标值时low向右移动。而且mid的更新要考虑在low high比较大的时候会不会整数溢出——写成low (high - low) / 2是更稳健的做法。这个点即使在2014年也已经是面试必考中的必考了但真实笔试时还是有人用(low high) / 2在极端测试用例下直接溢出成负数然后死循环或越界。int find_first(int arr[], int n, int target) { int low 0, high n - 1; while (low high) { int mid low (high - low) / 2; if (arr[mid] target) { high mid; } else { low mid 1; } } return (arr[low] target) ? low : -1; }这道题背后的安全思维是二分查找看着简单但在边界条件下容易出现不可预期的行为一旦数据集规模异常或目标值不在预期范围内程序可能越界、死循环进而被攻击者用精心构造的数据包打崩服务。我在后来做协议栈模糊测试时就反复遇到过因为解析函数里的二分查找在高频调用下出现off-by-one导致解析器命中非法内存地址的问题。出题人真正想看的不是你会不会二分而是你有没有边界条件就是安全边界的习惯。3.3 动态规划题一维DP如何变成空间优化陷阱最后一道算法题是典型的动态规划最长上升子序列LIS。题目描述大概是给定一个无序数组求最长的递增子序列长度。经典解法O(n^2)的DP是入门款但如果仅仅写这个按360当年的标准只能拿每道题一半的分。因为它的后续追问是能不能优化到O(n log n)如果不能请分析瓶颈在哪。O(n^2)的DP核心是dp[i]表示以第i个元素结尾的最长上升子序列长度转移时遍历前面所有j i如果a[j] a[i]则dp[i] max(dp[i], dp[j] 1)。空间是O(n)。O(n log n)的版本使用一个辅助数组tailstails[k]表示长度为k1的上升子序列中末尾元素的最小值然后对每个元素在tails里做二分查找决定插入位置。这道题考的不是背模板而是空间和时间的权衡。更隐蔽的一层是O(n log n) 的tails数组只是求出了长度并不能直接还原出最长上升子序列本身。如果题目变成了请给出最长上升子序列本身这个优化版本就需要额外记录前驱下标复杂度会上升不少。笔试现场我见过有人开心地写出了O(n log n)的长度解然后被追问你能输出这个序列吗时卡住。360的卷子上也明确在题目末尾加了一句如果只需要长度解释你的算法如果需要输出序列说明还需要额外做什么。 这考察的就是工程师对算法适用范围的边界感而不是单纯的解题能力。另外这道题放在安全公司还有个比喻层面的含义安全攻防本质上是一串决策序列的最优化问题——每一步你是选择阻断、放行、还是记录都会影响最终的安全态势。一个面向安全的工程师看待算法问题的视角不是我会背这个模板而是这个模板在什么条件不成立如果条件变了我需要如何处理。4. 操作系统与网络并发与协议的实战化考察4.1 进程和线程的辨析360的考法更接近系统底层操作系统部分的题目不算多但每一道都带有浓郁的底层色彩。有一道选择题给了一段文字描述多个任务需要共享同一份内存数据并且要求任意一个任务崩了不能拖垮其他任务让选择最合理的实现方式是多进程共享内存还是多线程共享变量选项里的干扰项还包括协程和消息队列。如果只从教科书层面看答案似乎是多线程——因为线程天然共享进程地址空间。但360这道题的微妙之处在于任意一个任务崩了不能拖垮其他任务这个条件。线程崩了通常整个进程都崩因为信号处理、段错误、异常通常都是进程级的而进程之间天然有地址空间隔离即使一个子进程段错误其他进程和父进程还能继续工作。所以更稳妥的答案是多进程 共享内存崩溃隔离性更好代价是通信和同步更复杂。这个考察点放在安全语境下特别有价值崩溃隔离是纵深防御的一部分。一个解析恶意文件的子进程崩了如果可以自动重启而主服务不受影响攻击者的思路就会从打崩它变成必须拿到代码执行权限难度提升一个量级。2014年时Chrome的沙箱设计、各类杀毒软件的扫描进程隔离都是基于这个逻辑。这道题表面在考进程线程概念实际在考察你有没有系统级的韧性思维。4.2 死锁的四个必要条件不是背出来就行死锁的经典四条件互斥、持有并等待、不可剥夺、循环等待基本上是送分题360肯定也考了——但它考得更实际。题目给出了几段伪代码场景要求判断哪几段代码可能死锁并指出破坏的是哪个条件。有一个场景特别典型线程A在持有锁L1时申请锁L2线程B在持有锁L2时申请锁L1。这个就是教科书级的循环等待持有并等待。死锁的解法看起来简单保证锁的获取顺序全局一致或者用trylock配合回退。但在真正的代码工程里锁的嵌套层级一深全局顺序就很容易被破坏——特别是当加锁代码分散在多个模块由不同的人维护时。360的这道题后面还藏了一个问题如果加锁顺序无法全局一致还有哪些减轻措施实质上的答案包括四个方向用单独的锁管理器统一管理锁层级、引入超时机制在获取不到锁时主动释放已有锁回退重试、缩小临界区降低锁持有时间、或者干脆改用无锁数据结构。安全公司看这段特别关注的是死锁往往导致服务不可用如果攻击者能通过构造特定请求让服务进入死锁这就是一个拒绝服务漏洞。2014年前后的不少Web中间件DoS漏洞就是精心构造并发请求触发锁顺序反转让worker线程全部卡死。所以这道题表面是并发编程基础实质是在提醒你锁用不好就是给攻击者递刀。4.3 网络协议题TCP为什么是三次握手而不是两次网络基础部分有一道非常经典的送命题为什么TCP建立连接是三次握手而不是两次为什么断开连接要四次普通公司基本问到这里就结束了但360的卷子还会追问假如两次握手会怎样请结合SYN攻击的机制说明。三次握手的核心意义在于客户端和服务端都要确认自己对端的接收能力和自己的发送能力是正常的。第一次握手服务端知道了客户端的发送能力正常第二次握手客户端知道了服务端的发送和接收能力都正常第三次握手服务端确认客户端知道自己的接收能力正常。如果只有两次握手服务端无法确认客户端的接收能力是否正常因为第一次握手后服务端就认为连接建立开始分配资源。攻击者可以借机伪造源IP发送大量SYN包服务端为每个伪造连接分配资源但永远收不到第三次握手资源耗尽导致正常用户无法访问——这就是SYN Flood。这个题放在安全公司的卷子里考察的是协议设计中的每一个往返都是安全性和性能的权衡。连接能否建立不是单纯的网络问题攻击者就是看准了协议设计中的资源分配时机在建立连接最耗资源的环节发难。如果你背过三次握手但从来没想过为什么不能省掉第三次那你在真实攻防中遇到协议层漏洞时就会缺乏从设计层面找攻击点的能力。5. 安全特色题从代码缺陷到漏洞利用5.1 缓冲区溢出的经典代码一眼看穿危险模式既然考的是360卷子里一定会有安全专向题目。印象最深的一道题给了如下代码片段要求找出其中的安全漏洞并说明如何利用void process_input(int sockfd) { char buf[64]; char *ptr buf; int n recv(sockfd, ptr, 256, 0); if (n 0) { log_error(recv failed); return; } buf[n] \0; printf(Received: %s\n, buf); }这道题坑点密集。最显眼的漏洞是recv最多接收256字节但buf只有64字节数据进来会直接溢出栈上的缓冲区。随后buf[n] \0也是一处越界写n可能等于256写入位置远超数组边界。第三个隐藏问题是printf(Received: %s\n, buf)如果buf里包含%s、%n等格式化占位符而代码没有把buf作为格式化字符串的格式参数——这里其实已经把buf安全地放在了%s的参数位置所以格式化字符串漏洞不算成立但出题人会问如果代码写的是printf(buf)会怎样那就是典型的格式化字符串漏洞可以读任意内存甚至写任意内存。这个题的完整答案其实要分三层。第一层指出缓冲区长度不匹配recv的缓冲区大小参数必须和buf的实际容量一致这是最基本的。第二层buf[n] \0即使改为边界正确也要注意n如果是负数recv返回-1的情况会向buf之前的位置写入\0破坏栈上的其他数据。第三层如何利用这个漏洞——攻击者发送超过64字节的恶意载荷覆盖栈上的返回地址把程序控制流劫持到shellcode的位置。2014年时NX和ASLR这些防护已经比较常见所以利用往往需要ROP链绕过DEP这时题目会进一步考察你对堆喷、栈喷射等技巧的理解。这道题对于非安全方向的同学来说也许背下应该用recv(sockfd, buf, sizeof(buf), 0)就够了。但如果你想进入安全行业必须能从攻击者的角度倒推如果我是攻击者这段代码哪里是我最喜欢的下手点缓冲区溢出之所以成为安全领域的经典漏洞正是因为太多开发者在写网络处理代码时默认数据不会比缓冲区大。而攻击者手里的牌恰恰是我发出的数据包长度完全可控。5.2 整数溢出如何绕过边界检查安全题的进阶分支安全特色题里还有一道整数溢出的题目它是这么设计的一个TLS解析器在解析服务端返回的证书长度字段时将从网络上读取的长度值直接传给mallocuint32_t cert_len; uint32_t remaining packet_size; memcpy(cert_len, packet_ptr, 4); if (cert_len remaining) { // error handling return -1; } char *cert_buf malloc(cert_len 1); memcpy(cert_buf, packet_ptr 4, cert_len); cert_buf[cert_len] \0;从表面逻辑看这里已经做了边界检查cert_len不大于remaining才继续分配。但如果cert_len是0xFFFFFFFF且remaining恰好也是0xFFFFFFFF比如整个数据包就是一个大文件解析到后面剩余长度是0xFFFFFFFF边界检查通过然后malloc(cert_len 1)传入0x100000000这个值在32位系统下会溢出成0malloc(0)可能会返回一个最小可用堆块接下来memcpy(cert_buf, ..., 0xFFFFFFFF)会往这个极小堆块里塞入4GB数据直接堆溢出崩溃甚至在特定堆布局下实现代码执行。这道题的考点是边界检查只看单一变量是不够的还要检查大小相加/相乘之后的最终结果。正确的做法是先判断cert_len 0xFFFFFFFF这样的极端值或者在加法前用cert_len SIZE_MAX - 1提前拒绝。这个模式在当时刚被广泛讨论因为多个知名CVE包括某些多媒体解析库、网络服务都栽在检查了但没检查完全的整数溢出上。至于网络字节序也不算偏差——题目里故意用memcpy读长度字段而没有做ntohl转换如果你在小端机上遇到大端发来的0x00000001会被读成0x01000000这也是一个隐藏的协议解析缺陷。解决方式是cert_len ntohl(cert_len)。这道题的综合度在整张卷子里最高它把整数溢出、大小端、堆内存管理串在一起考察的是协议解析代码中每一个数值都可能被攻击者控制的警觉性。5.3 那年的智力题/逻辑题安全公司想从里面看到什么笔试卷的最后一部分通常有少量逻辑题或脑筋急转弯网上不少人都说是送分题不直接算技术分。但我复盘360的卷子时发现他们的智力题也和普通公司不一样。普通的逻辑题会问一个人过桥需要多长时间这种经典的统筹问题360更倾向于给一个带边界的场景让你判断这个方案是否可行不可行时为什么。比如有一道题大概是设计一套方案让两个在不同房间的人在不借助第三方的情况下判断彼此是否持有同一个文件。不可行的方案包括互相发送文件的哈希值——因为A可以把哈希值发给BB比对后告诉A一致但A无法确认B没有伪装而且哈希值发送过程中可能被中间人篡改双方都无法验证来源。如果还要求防中间人就需要对称密钥或非对称签名的参与。这道题让我印象深是因为它已经隐约引入了密码学协议里互相认证和信道安全的概念。2014年时普通笔试卷里几乎不会出现这类问题而这在安全公司卷子里却被当作一种入门级的思维测试。它考察的核心是你脑子里有没有攻击者视角——很多安全方案失效不是因为算法不够强而是因为设计者默认了通信双方是可信的、信道是私密的、时间戳不会重放。这三个默认假设里任何一个在真实攻击中都可能被打破。6. 如果让我重做这份卷子时间分配与复健建议6.1 时间分配选择题快、编程题慢、安全题留足阅读时间2014年360的笔试是统一线上答题总时长不太长题目量又比普通笔试大。我当时的策略是选择题和填空题控制在总时间的40%以内因为这些题考察的是知不知道编程题和综合分析题用50%的时间因为这类题看的不是结果而是推导过程尤其是链表、二分、DP这类题目代码即使写对了注释和边界说明也能帮你拿分最后的10%用来检查特别是重新审视有没有漏掉的边界条件。如果让我重做我会在数据结构与算法那几道编程题上更克制一些。当时我在DP那道题上用了25分钟虽然写出来了优化版本但消耗了太多时间导致后面的安全题只剩大致15分钟。安全题看起来都是选择题或判断改错但实际上每道题都需要你读懂一段代码、找出漏洞、写出修改方案15分钟根本不够。复盘下来正确的时间分配应该是算法题15分钟兜底把省出的时间匀给安全专项题——那些题目的信息密度高你读题和写解答的深度直接影响面试官对你的判断。6.2 基础功力的复健从笔试反推必备的知识清单这套卷子做完以后我最大的感受是应试刷题短期内也许管用但真正走到安全岗位后卷子上的每个考点都会转化为真实工作中每天要面对的问题。比如sizeof和strlen的区别在我后来review过的大量C代码里反复出现缓冲区溢出在2020年代的今天仍然排在CWE Top 25里整数溢出造成的漏洞每年依然有新的CVE案例。这说明2014年的360校招卷子在技术选型上是很超前的——它没考花哨的新框架、新语言而是考那些二十年没变过的底层问题。如果你也想通过这类笔试卷来检验自己的基础功力我建议按这个清单自查能否完全说清sizeof、strlen、strcpy、memcpy的边界行为能否手写链表的插入、删除、反转并说出递归和迭代各自的栈开销能否用O(n log n)实现LIS并解释为何不能直接还原序列能否分析死锁的四个必要条件在你的实际项目中各自对应的代码能否在一段假的协议解析代码里找出3个以上安全隐患并给出修复这些能力没有一项是刷题刷出来的肌肉记忆。它们更需要你在平时写代码时多问自己一句如果数据不是预期格式如果输入长度不是预期范围如果外部环境发生异常我的代码会不会崩、会不会泄露、会不会被劫持控制流安全的本质不是多背了几个漏洞类型而是在写每一行代码时都对边界保持敬畏。如果你在2024年再回头看这份2014年的卷子你会发现它的知识点几乎没有过时。C语言没有过时内存安全没有过时协议解析的边界问题没有过时。变化的只是攻击手法更加复杂、防护机制更加立体——这恰恰说明当年这份笔试卷筛选的并不是知道答案的人而是对代码脆弱性有直觉的人。如果你的直觉还没建立起来不妨找几段真实的生产代码像做这套卷子一样一块一块拆开看。拆得足够多以后你会慢慢形成条件反射看到memcpy就想长度看到malloc就想释放看到recv就想缓冲区。那时候不管是笔试还是实战你都已经站在正确的一边了。