“360公司-2019校招笔试-Windows开发工程师客观题合集”这个标题,乍一看像是一个简单的题目汇总帖,但真正经历过那场笔试的人会明白,这几乎就是一场针对Windows系统底层功底的大排查。360作为安全公司,对Windows开发工程师的要求从来不是“会调几个API”那么简单,而是真的需要你对进程、内存、PE结构、消息机制这些东西有深入理解。这篇文章我会结合当时笔试的题型结构、典型考点以及我自己的复盘经验,把这场客观题笔试掰开了讲清楚,希望能给准备Windows开发岗校招的同学一些真正有用的参考。
1. 笔试整体结构与考察逻辑分析
1.1 题目构成与时间分配
先说说这套题的整体构成。360校招的笔试客观题部分,通常在60到90分钟之间,题量大约在40到60道,以单选题和多选题为主,部分年份会夹带少量判断题。Windows开发工程师岗位的题目,核心考察方向非常聚焦:C/C++语言基础、操作系统原理、Windows系统机制、数据结构与算法、网络基础,以及安全相关的常识性内容。
时间分配上,我的感受是前30分钟一定要把C/C++和数据结构的题快速过掉,这类题答案相对明确,不需要反复推敲。中间20分钟留给Windows系统机制相关的题,这部分需要仔细读题,容易混淆概念。最后的时间留给网络和多选。多选是丢分重灾区,少选错选都不得分,所以不确定的选项宁愿不选,也不要乱猜。
提示:笔试系统一般不允许回退修改,至少我当时遇到的系统是分模块作答、分模块提交,所以做题节奏要稳,不要指望回头检查。
1.2 360出题风格:安全公司视角下的Windows开发
360的笔试题目和普通互联网公司的Windows开发岗有一个明显区别——它带有强烈的安全视角。比如同样考进程,普通公司可能问“进程和线程的区别”,360可能会问“进程注入的常见方式有哪些”或者“如何遍历进程模块”。同样考内存,普通公司问“堆和栈的区别”,360可能会问“堆溢出利用时通常需要绕过哪些保护机制”。
这意味着,如果你只是按部就班地刷了一遍《Windows核心编程》,对安全攻防一无所知,有些题做起来会感觉像是在看天书。我当时就栽在几道关于PE结构、导入表、重定位表的题上,这些知识点在安全领域是基本功,但在普通Windows开发中,很多人根本不会深究。
所以,准备360的笔试,不能只站在“开发工程师”的角度去复习,还必须了解一些安全基础,至少要知道缓冲区溢出、DEP、ASLR这些名词代表什么,为什么它们重要。
1.3 客观题不等于简单题
这里必须提醒一句:客观题不代表简单。很多同学觉得笔试客观题就是基础题,随便刷刷就能过,这是误解。360的客观题很多是“看似基础,实则暗藏陷阱”的题。比如“在Windows中,以下哪个API用于创建线程”这样的题,选项里可能同时出现CreateThread、_beginthread、CreateRemoteThread、CreateProcess,如果对这几个API的适用场景不清楚,很容易选错。
另外,多选题的计分规则也值得注意。360的多选题通常是“全部选对才得分,少选、错选、多选均不得分”。这个规则非常残酷,意味着你必须在每个选项上都足够确定,才能拿分。我当时的策略是:如果一道多选题里有两个选项特别陌生,干脆整道题放弃,把时间留给后面的题。笔试是分数游戏,不是学术研究,学会取舍很重要。
2. 核心考点拆解:C/C++与操作系统基础
2.1 指针、内存管理:几乎必考的几类题
C/C++是Windows开发的根基,也是笔试的重头戏。从真题回忆来看,指针和内存管理相关题目能占到C/C++部分的60%以上。常考的类型有这么几类:
第一类是“指针与数组的关系”。比如给出一段代码,定义一个二维数组int a[3][4],问a+1和&a+1的区别。这类题考察的是指针运算的步长概念。a是数组首地址,类型为int()[4],所以a+1跳过4个int;&a是整个数组的地址,类型为int()[3][4],所以&a+1跳过整个数组12个int。很多人在这里栽跟头,是因为混淆了数组名和&数组名的类型。
第二类是“内存泄漏与悬空指针”。题目可能会给出一个函数,其中malloc了一块内存,在某个分支里提前return了,问是否会导致内存泄漏。这类题看起来简单,但考察的是你有没有“所有出口都要释放资源”的意识。Windows开发中经常用到句柄、内存、GDI对象等资源,忘记释放就是泄漏,笔试考这个非常务实。
第三类是“结构体对齐”。这个在Windows开发中尤其重要,因为很多Windows API的参数结构体都有字节对齐要求。笔试常考的是给定一个结构体,让你计算sizeof的值。这类题的关键是理解默认对齐规则和#pragma pack的影响。举个例子,一个结构体包含char、int、char三个成员,在默认4字节对齐下,sizeof是12而不是6,因为第二个char后面要填充3个字节,使整个结构体大小对齐到4的倍数。
实操心得:结构体对齐的题,我在笔试中遇到过至少两次,一次是直接计算sizeof,另一次是问“以下哪个结构体定义在作为IPC消息传递时更节省空间”。这提醒我们,复习时不仅要会算,还要理解对齐在跨模块通信中的实际影响。
第四类是“虚函数与多态”。考察虚函数表(vtable)的布局、虚析构函数的作用等。这类题需要你画得出虚函数表的结构,知道一个类对象的地址开头4/8个字节是指向虚表的指针。笔试不会考太深,但“基类析构函数为什么必须是虚函数”这种题几乎年年有。
2.2 进程、线程与同步机制:从理论到Windows实现
操作系统原理是Windows开发工程师笔试的另一大块。常考的知识点包括进程状态转换、线程调度算法、死锁产生的四个必要条件、信号量与互斥量的区别等。但360的题不喜欢直接背概念,它更倾向于让你在Windows API的语境下去选择正确的机制。
比如问“以下哪种同步对象可以实现跨进程同步”,选项是临界区、互斥量、事件、信号量。这题考察你对Windows同步对象的理解:临界区只能用于同进程内线程同步,而互斥量、事件、信号量都是内核对象,可以跨进程。我见过不少复习资料说“临界区是轻量级的”,但很少强调它不能跨进程,笔试就专门考这种容易被忽略的边界。
再比如“线程本地存储”。Windows中可以用TLS(Thread Local Storage)让每个线程拥有独立的数据副本,笔试可能会问TLS的回调函数在什么时机被调用。这个问题的答案是在DLL附加和分离的时候,TLS回调会被执行。这实际上是DLL注入检测的一个知识点,360作为安全公司,对DLL加载过程、TLS回调这类细节非常敏感。
多线程题目还有一种考法,就是给出一段多线程代码,问最终结果。比如两个线程分别对同一个全局变量执行i++操作10000次,问最终i的值。正确答案是“不确定,可能小于20000”。原因是i++不是原子操作,它包含读取、加一、写回三步,两个线程可能同时读取到同一个旧值,导致丢失更新。如果你只背了“i++不是原子操作”这句话,没有理解其内部机制,遇到变偏的题还是会出错。
2.3 Windows系统机制:DLL、消息循环、注册表与PE结构
这部分是360笔试区分度最高的地方,也是普通Windows岗位和360 Windows岗位的分水岭。 DDL、PE、消息循环、注册表是几个绕不开的关键词。
消息循环的题一般比较简单,比如“以下哪个函数负责从消息队列中取出消息”,答案是GetMessage或PeekMessage,而TranslateMessage负责键盘消息转换,DispatchMessage负责把消息分发到窗口过程。但360可能会问得深一点:“在窗口过程中,DefWindowProc的作用是什么”,或者“PostMessage和SendMessage的区别”。前者是默认处理未处理消息,后者是异步投递与同步发送的差别。这提醒我们要把“消息循环”放在一个完整的链路里去理解,而不是孤零零地背API函数名。
DLL相关的题就更有安全味了。比如“DllMain中不应该做哪些操作”。标准答案是不要在DllMain里加载其他DLL、创建窗口、等待线程结束等,因为DLL加载阶段可能还没准备好消息循环,而且持有加载器锁时执行这些操作容易死锁。这个知识点,安全工程师在分析DLL劫持、DLL注入时经常遇到。笔试考它,本质上是筛选那些不止会“写一个导出函数”的人,而是对DLL生命周期有全面认知的人。
PE结构是另一个高频考点。常考的是:DOS头、NT头、节表、导入表、导出表里分别有什么。比如“导入表(Import Table)的作用是什么”,答案是记录该PE文件依赖了哪些外部DLL和函数。如果笔试还有安全属性的加试题,还可能会问“、入口点(AddressOfEntryPoint)被修改通常意味着什么”,这在逆向中就是常见的加壳和恶意代码入口指向。
注册表相关的题这些年出现频率有所下降,但偶尔还会考。比如“以下哪个注册表根键用于存储当前用户配置”,答案是HKEY_CURRENT_USER。还有“服务程序通常在哪个键下注册”,答案是HKLM\SYSTEM\CurrentControlSet\Services。这类题简单,但不能丢分。
3. 数据结构、网络基础与安全常识
3.1 数据结构与算法:客观题里的常客
数据结构和算法是校招笔试必然出现的模块,360也不例外。但客观题部分不会考太复杂的算法,基本都是选择题形式出现的基础内容。
常考的点包括:二叉树的先序/中序/后序遍历顺序、哈希表的冲突解决方法、栈和队列的应用场景、排序算法的平均时间复杂度和稳定性。360偶尔会出一些“给定一组数据,问快速排序第一趟后的结果”这样的题,考察对排序过程的理解。这类题没有捷径,只能是多练。
一个有意思的现象是,360笔试的算法题有时会和Windows特性结合。比如“哪个数据结构最适合实现LRU缓存”,答案是“哈希表+双向链表”。这就可以延伸到Windows内核中文件缓存的管理方式。如果只看常规的《数据结构》教材,不关注它在实际系统中的应用,这类题容易选错。
实操心得:复习数据结构时不要只刷题,要关注每种结构在操作系统里的实际应用场景。比如消息队列本质是一个队列,进程的虚拟地址空间管理用到了红黑树,内存池用到了链表,这些都可能是出题背景。
3.2 网络协议与Socket编程:覆盖基础,但不会太深
Windows开发岗笔试的网络题不算难,主要集中在TCP/IP协议栈的基础层面。常考的知识点包括:TCP三次握手和四次挥手流程、TCP和UDP的区别、HTTP和HTTPS的端口与加密方式、DNS解析过程等。
TCP的状态图是高频考点,尤其是“TIME_WAIT状态出现在哪一端、为什么要等待2MSL”。在Windows开发中,如果你写了一个服务端程序,主动关闭连接后端口被占用,原因就是TIME_WAIT在起作用。笔试可能会给出一个情景,让你判断哪个状态持续时间最长,这时候需要你对状态转换图足够熟悉。
Socket编程的题主要考Windows下Winsock的基本流程:WSAStartup初始化、socket创建、bind绑定、listen监听、accept接受、recv/send收发、closesocket关闭。偶尔会问你“在Windows中,WSAStartup函数的作用是什么”,答案是协商版本并初始化Winsock库。这类型题只要写过一次网络程序,基本不会错。
360可能会额外考察一些和“扫描、监控”有关的网络知识,比如“系统中有大量SYN_RECEIVED状态的连接,推测可能发生了什么”,这就是典型的SYN Flood攻击特征。从一个网络基础考点过渡到安全检测思路,是360出题的一贯风格。
3.3 安全基础:Windows开发者需要具备的安全直觉
这部分是360笔试的“特色菜”,也是我最想强调的部分。网上很多Windows开发岗笔试汇总里都会提到缓冲区溢出,但很少深入解释为什么安全公司要考这个。我个人的理解是,如果一个开发工程师在写代码时脑子里没有攻击模型,他在处理用户输入、内存拷贝、文件解析这些操作时就会非常危险。360的审美里,Windows开发工程师首先得是“安全的开发者”。
缓冲区溢出是绝对的高频考点。典型的题目是“以下哪个函数存在缓冲区溢出风险”,选项包括strcpy、strncpy、memcpy、sprintf_s。正确的答案是strcpy和sprintf,因为它们不检查目标缓冲区的大小,而strncpy和memcpy需要传入长度参数,sprintf_s是安全版本。还有更进阶一点的问法:“在栈缓冲区溢出攻击中,攻击者通常会覆盖什么数据来劫持控制流”,答案是函数返回地址或SEH链,这是老式栈溢出利用的经典手法。
权限提升是另一个考点。普通用户与管理员权限的差异、UAC的作用、为什么需要以管理员身份运行某些程序,这些都可能以情景题出现。比如“在Windows中,注册表HKLM和HKCU的区别,哪个普通用户可写”,如果理解权限模型,就能答出来。
当然,不是非要你精通漏洞利用,而是理解这些基本安全概念,在开发中有意识地避免高危模式。客观题主要考常识和术语,不会让你写exploit。
4. 实操复盘:典型题目与答题思路
4.1 几道印象深刻的真题回忆与解析
我整理了几道当时考场上印象比较深的题,题目原文我无法百分百还原,但考点和解题思路可以明确分享。
第一道是“C++中,以下哪一个不是多态的必要条件”。选项大概是:继承、虚函数、基类指针或引用调用、函数重载。答案是函数重载。因为多态是运行时行为,函数重载是编译期静态绑定,不满足“运行时动态决定调用哪个版本”这个条件。这类题考察的是C++核心概念之间的界限。
第二道是“在Windows中,PostMessage和SendMessage的差别”。选项大概有“PostMessage把消息放入消息队列后返回,SendMessage直接调用窗口过程等消息处理完成才返回”、“两者都是异步的”、“SendMessage不能跨进程”、“PostMessage不会失败”。正确答案是第一个。这个知识点在Windows编程里太基础了,但很多人只记了“Post是异步,Send是同步”,没有深挖跨进程时的行为差异。如果题目延伸问“SendMessage跨进程发送时会发生什么”,答案是发送方会阻塞,接收方由系统代为执行窗口过程,这在有些反死锁设计中是一个需要特别处理的点。
第三道题是“以下关于DllMain的说法,正确的是”。正确选项是“当进程或线程加载/卸载DLL时会调用DllMain”,而“可以在DllMain里创建窗口”之类的选项是错误的。这道题给了我们一个提示:安全公司非常希望开发工程师理解DLL的生命周期,因为DLL注入、DLL劫持攻击都发生在加载阶段,如果开发者对加载流程不敏感,很难写出对抗性的防御代码。
第四道题,我印象更深刻,“关于Windows内核对象,以下哪些说法是正确的”。多选,选项包括“内核对象可以被多个进程共享”、“调用CloseHandle会立即销毁内核对象”、“内核对象的引用计数在句柄复制时会增加”、“事件、互斥量、信号量都是内核对象”。正确答案是1、3、4。第2项是经典陷阱:CloseHandle只是递减引用计数,只有当引用计数为0时对象才会被真正销毁。这道题如果没有理解“句柄是对象表的索引,对象本身有引用计数”这个概念,一定选错。
第五道题是网络方向的,“TCP连接中关闭连接时,主动关闭方最后进入的状态是什么”。答案是TIME_WAIT。这道题对应的考点我已经在前面拆解过了。延伸题常问“TIME_WAIT持续多久”,答案是2MSL,具体数值默认为240秒(4分钟)左右,而MSL在不同系统实现中不同,Windows下的默认MSL是120秒。360笔试可能会在这个基础上问“为什么需要TIME_WAIT”,确保旧连接的数据包不会混入新连接,并能正常关闭双向通道。
4.2 答题技巧:如何提高客观题的得分率
基于我当时的做题经验,有几个技巧值得分享。
第一,先做单选的确定题,再做多选。单选里“绝对确定答案”的题要快速拿下,让心态稳下来;多选放在后段,用充足时间推敲。
第二,多选题遇到不太确定的选项时,采用“保守策略”。360计分规则是全部选对才能得分,所以宁缺毋滥。如果你能排除两个错误选项,剩下两个即使不太确定,也可以优先尝试选上。但如果你只认识一个选项,其他三个都陌生,优先只选那一个。虽然得不到满分,但也不会因为错选而失分(按常见的计分规则)。不过要注意,如果系统采用“漏选得部分分”规则,那策略又可以调整为“尽量多选”。不同规则的考场策略不同,拿到试卷先看一眼计分说明。
第三,C/C++题遇到代码类题目,不要只凭“感觉”,一定要在草稿纸上手动模拟执行一遍。很多指针、内存的题,手推一遍结果和想象中完全不同。比如前面提到的二维数组指针偏移,画一张图就清楚了。
第四,时间管理上不要恋战。一道题如果超过3分钟还没思路,直接标记并跳过。笔试不是非得满分,而是要在有限时间内拿到最多分数。一道难题卡住,可能导致后面5道基础题没时间做,这是最亏的交易。
注意:笔试系统如果支持“分模块提交”,那么每个模块的题一旦提交就不能修改了。做的时候要集中注意力,不要做着后面的题想着前面的题。
5. 备考建议与资源整理
5.1 针对Windows开发岗的复习路线
如果你现在正准备360或类似安全公司的Windows开发岗,我给你一个从零开始的复习路线参考。
第一步,打牢C/C++基础。重点复习指针、内存布局、结构体对齐、虚函数、STL容器的底层实现。教材推荐《C++ Primer》和《Effective C++》,但不是通读,而是聚焦在内存和对象模型相关的章节。
第二步,系统学习Windows核心编程。最经典的是《Windows核心编程》(第五版),重点关注进程、线程、作业、内存映射文件、DLL、线程同步、Windows消息机制这几章。这本书很厚,但笔试考点就集中在这几张。配合《Windows程序设计》理解消息循环和GDI相关基础。
第三步,补充安全基础。这一点很多人会忽略,但结合360的岗位特点,非常重要。推荐看《加密与解密》(第4版)中的PE结构、缓冲区溢出、DLL注入相关章节。如果觉得这本书太厚,至少要用《PE权威指南》或在线资源把PE文件格式搞清楚。栈溢出部分需要理解函数调用栈布局、返回地址劫持原理。不需要会写exp,但要能看懂攻击原理。
第四步,过一遍网络基础。推荐《计算机网络:自顶向下方法》的前四章,重点掌握TCP三次握手/四次挥手、TCP状态转换、TCP和UDP的差异、HTTP协议基础。如果还想加分,额外了解DNS解析过程和HTTPS握手流程。
第五步,刷题。不要只刷360的题,其他公司的Windows开发岗笔试题目也可以刷。推荐牛客网上的C/C++、操作系统、计算机网络题库,按专题刷,每天50道左右。关键不是刷了多少,而是把错题背后的知识点彻底搞懂。
5.2 避坑指南:这些复习误区要避开
误区一:只背概念,不写代码。笔试虽然是客观题,但你写出过真实的Windows程序,对API的记忆会深得多。我当时备考时给自己写了一个小型文件管理器,涉及窗口创建、消息循环、树控件的节点遍历、文件系统API的调用,这些经验直接让我在关于消息循环和控件通知的题目上没有任何犹豫。
误区二:轻视多选。单选可以通过排除法蒙对,多选蒙对的概率极低。复习时遇到“下列关于……的说法正确的是”这类题型,不要只看正确选项,还要分析为什么其他选项是错的。这样到了考场上,面对变体选项,你才能做出判断。
误区三:只看Windows编程书,不碰编译原理和汇编。360的试题背后有很强的逆向思维倾向,如果你完全不了解寄存器、栈、汇编指令,那PE结构、缓冲区溢出这些考点就很难拿分。不需要精通,但至少要知道push、call、ret、leave这些指令的大致作用,知道函数序言(prologue)和收尾(epilogue)是什么。有了这层知识,再看“栈溢出覆盖返回地址”的题目就一目了然了。
误区四:不刷真题。360每年的题目会变,但出题风格和考察范围相对稳定。如果能在笔试前找到往年的面经和笔经回忆,哪怕只是列表,也能帮你圈定重点。我当时就是利用论坛上网友分享的考点回忆,发现了PE结构是必考点,然后专门花了一个周末把PE解析器的代码跟着写了一遍,后来考场上就遇到了导入表的题。
5.3 考试当天的一些实用经验
最后聊一聊考试当天的细节。校招笔试一般是线上笔试,需要一个安静的环境和稳定的网络。开考前半小时,把摄像头、麦克风调试好,浏览器最好用Chrome或Edge,并确保允许弹窗和屏幕共享。360的笔试系统对浏览器兼容性要求不低,每年都有同学因为浏览器不兼容导致扫码登录失败。
笔试过程中,准备好草稿纸和笔,很多数据结构和指针题需要手推。调试好IDE并不需要,因为客观题不写代码,但如果有Python或C++的本地环境,可以快速验证一些计算题,前提是笔试系统允许。绝大多数情况笔试网站自带编译运行功能,但不一定开放给客观题部分,不要在这上面浪费时间。
做题时心态要放平。360的笔试难度即使在985高校的同学中也是能拉开差距的,遇到不会的题很正常。我当时大概有15%的题是完全不确定的。重要的是把确定的题全做对,把不确定的题用策略保住部分分数,整体分数就不会差。
笔试只是第一关,但客观题的成绩直接影响后续能否进入面试环节。360面试会更深入地追问你笔试中答错的题,所以笔试结束后,趁记忆清晰,赶紧把拿不准的题目记下来,回去翻书搞懂。我当年就因为在笔试中对“DllMain中不应调用Sleep”这道题记忆深刻,面试时被问到DLL加载流程时从容答上来了。
写在最后
准备360这类安全公司的Windows开发岗,本质上是在“开发”和“安全”的交汇处建立知识体系。客观题虽然形式上是选择题,但它考察的是你的知识边界到底在哪里。如果你只满足于“能调API、能写出能跑的代码”,还不够;你还需要知道这些API背后的实现原理,知道系统机制有哪些脆弱点,知道恶意代码通常从哪里下手。
刷题不是目的,建立体系才是。把这套笔试当作一面镜子,照出自己知识体系里最薄弱的部分,然后逐个击破。这个过程虽然辛苦,但当你拿着一个自己动手解析过的PE结构、自己写过多线程同步代码、自己排查过内存泄漏问题的经验去参加笔试时,那些题目就不再是考题,而只是你日常工作的延伸。
愿每一个认真准备Windows开发的校招生,都能拿到心仪的Offer。这套客观题合集,既是你复习的捷径,也是你查漏补缺的地图。