編程到安全對抗)
奇安信做安全的同行應(yīng)該都不陌生國內(nèi)終端安全領(lǐng)域繞不開的一家。2020年那一陣子客戶端開發(fā)工程師的筆試題目在網(wǎng)上被傳了不少我掃了一遍第一感覺是這題和普通互聯(lián)網(wǎng)公司考的那套完全不是一個(gè)路子。它不是在考你會不會寫界面、調(diào)接口而是在考你懂不懂Windows系統(tǒng)本身懂不懂一個(gè)安全軟件在用戶機(jī)器上是怎么活下來的。尤其有意思的是從相關(guān)的搜索記錄里能看到幾類非常典型的需求有人搜“vs2015 開發(fā)windows服務(wù)”有人搜“奇安信 輸入驗(yàn)證路徑遍歷”還有一大片關(guān)于“天擎卸載”的搜索。把這些線索串起來基本就能還原出這套題目背后的崗位畫像要做的不是一個(gè)普通桌面軟件而是一個(gè)常駐系統(tǒng)、要和各種惡意程序貼身對抗、還得在千奇百怪的機(jī)器上穩(wěn)定運(yùn)行多年的Windows客戶端。這篇文章就針對這套題目背后真正的技術(shù)考點(diǎn)做一個(gè)盤點(diǎn)和拆解。不管你是打算投奇安信還是準(zhǔn)備任何安全廠商的Windows客戶端崗位甚至只是想做一款用戶量還不錯的Windows桌面軟件都值得對照著往下看看。1. 奇安信這批題目背后藏著一個(gè)什么樣的客戶端團(tuán)隊(duì)1.1 先搞清楚公司做什么再看題目考什么奇安信的產(chǎn)品線很寬但和客戶端開發(fā)工程師直接相關(guān)的主要是這幾塊天擎終端安全管理系統(tǒng)、企業(yè)安全瀏覽器、代碼衛(wèi)士源碼靜態(tài)掃描、安全準(zhǔn)入、終端檢測與響應(yīng)EDR之類的模塊。其中天擎是絕對的主力產(chǎn)品企業(yè)用戶裝的最多。而客戶端開發(fā)工程師進(jìn)去之后大概率就是跟這些終端軟件打交道。終端安全軟件的業(yè)務(wù)特征是什么我用一句話總結(jié)它是個(gè)常住在操作系統(tǒng)里的特殊房客要看得到別的進(jìn)程在干什么要管得住別的進(jìn)程不讓它們亂來還得保證自己不被惡意軟件干掉更要在用戶重啟、升級、殺毒軟件沖突、磁盤快滿、網(wǎng)絡(luò)斷連這些亂七八糟的情況下繼續(xù)工作。這樣的業(yè)務(wù)屬性決定了筆試出題的口味。普通互聯(lián)網(wǎng)公司考的是數(shù)據(jù)結(jié)構(gòu)、網(wǎng)絡(luò)協(xié)議、業(yè)務(wù)架構(gòu)設(shè)計(jì)而奇安信這類安全廠商的Windows客戶端崗位題目一定會往操作系統(tǒng)底層靠而且會夾雜大量“如果遇到XXX怎么辦”的實(shí)戰(zhàn)場景題。1.2 安全客戶端和普通桌面軟件技術(shù)側(cè)重點(diǎn)差在哪我見過不少做桌面軟件出身的朋友去面安全廠商的時(shí)候特別不適應(yīng)。原因很簡單日常做業(yè)務(wù)軟件很多系統(tǒng)級的東西是可以繞過去的但安全軟件繞不過去。舉個(gè)例子。普通桌面軟件想監(jiān)控一個(gè)目錄下的文件變化調(diào)ReadDirectoryChangesW就夠了拿到通知更新一下界面就算完事。但安全軟件要監(jiān)控全盤文件行為要攔截可疑進(jìn)程對文件的修改光靠用戶態(tài)API根本不夠Windows在Vista之后提供了更底層的文件系統(tǒng)過濾機(jī)制想做得深就得往驅(qū)動層走。雖然筆試不會要求你直接寫一個(gè)minifilter驅(qū)動但候選人得知道這條路的存在知道用戶態(tài)方案和內(nèi)核態(tài)方案的邊界在哪。再說進(jìn)程管理。普通軟件想結(jié)束一個(gè)進(jìn)程調(diào)TerminateProcess就完了。但安全軟件這么做會被反殺你得先想辦法把自己提權(quán)到足夠高的權(quán)限處理掉目標(biāo)進(jìn)程的令牌再考慮守護(hù)進(jìn)程怎么避免被殺后無法自愈。所以看奇安信這批題目的時(shí)候我建議別只盯著具體某道題的答案而是先建立一個(gè)大框架這類公司需要的是懂系統(tǒng)機(jī)制、能處理對抗場景、代碼習(xí)慣極其嚴(yán)謹(jǐn)?shù)墓こ處?。下面的?nèi)容全部圍繞這個(gè)大框架展開。2. 2020年前后Windows客戶端開發(fā)的看家本領(lǐng)2.1 C是基本盤但這里考的和學(xué)校教的不一樣Windows客戶端開發(fā)尤其是安全終端方向的客戶端C依然是絕對的主力語言。這不是說學(xué)校里教的《C程序設(shè)計(jì)》沒用而是說到了崗位筆試和實(shí)際開發(fā)層面考察的重心完全變了。學(xué)校重點(diǎn)考語法、考面向?qū)ο笕筇匦?、考設(shè)計(jì)模式而實(shí)際崗位里最要命的是這幾塊內(nèi)存管理誰申請誰釋放、RAII是不是形成習(xí)慣了、STL的底層行為和線程安全性、C11/14之后引入的新特性你會不會在實(shí)際項(xiàng)目里用、異常安全、以及最容易被忽略的——各種隱式類型轉(zhuǎn)換和未定義行為。我見過一道很典型的題給一段在Windows服務(wù)里跑多線程任務(wù)隊(duì)列的代碼讓候選人指出哪里有內(nèi)存泄漏、哪里有數(shù)據(jù)競爭。內(nèi)核里那些對象還好說最坑的是STL容器在多線程下又沒加鎖又發(fā)生了迭代器失效這種代碼在筆試階段靠眼睛看不出來得上手寫才能暴露問題。所以我的建議是準(zhǔn)備這類崗位時(shí)把C的基礎(chǔ)牢靠程度放在第一位算法題反而不是決勝點(diǎn)。2.2 Win32與系統(tǒng)機(jī)制安全產(chǎn)品繞不開的底層能力如果說C是武器那Win32編程就是戰(zhàn)場地圖。這里說的Win32不是說你得把每一條API簽名都背下來而是要對Windows提供的幾個(gè)核心機(jī)制有系統(tǒng)級理解。第一是窗口與消息機(jī)制。消息循環(huán)不是簡單的GetMessage/DispatchMessage循環(huán)要理解消息的優(yōu)先級、隊(duì)列消息與非隊(duì)列消息的區(qū)別、SendMessage和PostMessage對調(diào)用線程的影響以及為什么安全軟件里大量后臺邏輯要盡量少依賴UI線程的消息循環(huán)。第二是進(jìn)程與線程同步。臨界區(qū)、互斥量、事件、信號量這四樣?xùn)|西各適用什么場景臨界區(qū)不跨進(jìn)程互斥量可以跨進(jìn)程還可以設(shè)等待超時(shí)事件適合做一對多通知信號量適合做資源計(jì)數(shù)。安全軟件里到處都是多進(jìn)程多線程協(xié)作這些東西是必考。第三是動態(tài)鏈接庫。為什么安全軟件要把核心邏輯抽成DLLDLL的導(dǎo)出表、加載順序、依賴地獄問題還有DLL注入與劫持——這對安全軟件既是能力也是風(fēng)險(xiǎn)。2020年那會兒DLL側(cè)加載已經(jīng)是很常見的攻擊手法了客戶端開發(fā)工程師如果連LoadLibrary的搜索順序都說不清楚肯定是不及格的。第四是文件、注冊表、服務(wù)控制管理器這三類對象的安全屬性。安全軟件有自己的文件要保護(hù)有自己的服務(wù)要管理對這些對象的安全描述符要有概念否則別說對抗惡意軟件連自己產(chǎn)品的自我保護(hù)都做不好。2.3 組件化開發(fā)、UI框架與工程化構(gòu)建的取舍很多準(zhǔn)備筆試的人會有個(gè)誤區(qū)覺得客戶端開發(fā)就是做界面所以花大量時(shí)間去刷Qt或Electron的UI特效。但實(shí)際上安全類Windows客戶端崗位筆試基本不考UI特效反而會關(guān)心你對工程化的理解。以天擎這類終端產(chǎn)品為例它的客戶端是個(gè)非常復(fù)雜的程序起碼包括托盤界面進(jìn)程、核心服務(wù)進(jìn)程、升級進(jìn)程、日志上報(bào)模塊、各類策略執(zhí)行模塊有些模塊還是驅(qū)動態(tài)。這堆東西要用什么UI框架不同團(tuán)隊(duì)有不同的選擇。老一代的產(chǎn)品大量用MFC因?yàn)闅v史包袱重、穩(wěn)定、體積小換個(gè)新框架成本極高。新一點(diǎn)的模塊會自然傾向Qt跨平臺、開發(fā)效率高、現(xiàn)代感強(qiáng)。少數(shù)團(tuán)隊(duì)用DuiLib或自研DirectUI方案因?yàn)榻缑嫫つw自由度大。我把這幾條路線列個(gè)表方便你理解為什么面試官喜歡問“你在項(xiàng)目里怎么選型”而不是“你會不會用某個(gè)框架”UI路線優(yōu)勢劣勢典型場景MFC穩(wěn)定、資料多、對Win32封裝淺、體積小界面老舊、開發(fā)效率低歷史遺留模塊、維護(hù)為主Qt跨平臺、組件豐富、信號槽機(jī)制清晰體積略大、需要處理發(fā)布依賴新模塊、工具類界面DuiLib/DirectUI自繪、皮膚效果強(qiáng)、靈活性高坑多、社區(qū)生態(tài)弱安全軟件主界面、需要酷炫交互的模塊純Win32 SDK最輕量、無依賴開發(fā)效率極低小工具、測試程序2020年那陣子Windows 7和Windows 10還處于共存期Win10占比持續(xù)上升所以UI工程還要考慮DPI縮放、高對比度主題這些兼容性問題。筆試?yán)镏苯涌伎蚣蹵PI的可能性很小但你得說得清楚“自己項(xiàng)目里為什么選這個(gè)框架怎么解決它帶來的問題”這才是面試官想聽的。3. 從熱搜里的線索看這些題真正在考什么3.1 “vs2015 開發(fā)windows服務(wù)”服務(wù)程序是隱藏大項(xiàng)搜索熱詞里出現(xiàn)“vs2015 開發(fā)windows服務(wù)”絕對不是偶然。Windows服務(wù)是安全終端軟件最常用的載體。為什么道理很簡單服務(wù)可以設(shè)置為開機(jī)自啟動而且能在用戶登錄之前就跑起來服務(wù)默認(rèn)以SYSTEM或Administrator權(quán)限運(yùn)行能拿到比普通用戶進(jìn)程更高的權(quán)限服務(wù)還能配置失敗自動恢復(fù)。對于需要第一時(shí)間接管系統(tǒng)、始終在線監(jiān)控的安全軟件來說服務(wù)幾乎是唯一選擇。很多人第一次寫Windows服務(wù)會對著VS2015發(fā)愣因?yàn)镃工程和C#工程不太一樣C#有現(xiàn)成的Windows服務(wù)項(xiàng)目模板一鍵生成C這邊沒有那種省事的模板。通常是在一個(gè)Win32工程里手寫服務(wù)的入口邏輯也就是實(shí)現(xiàn)ServiceMain、注冊Service Control Handler、調(diào)用StartServiceCtrlDispatcher然后用SCM相關(guān)API去安裝和啟動。筆試如果出服務(wù)相關(guān)題目最常踩的坑有兩個(gè)。一是服務(wù)沒有在啟動后的30秒內(nèi)調(diào)用StartServiceCtrlDispatcherSCM直接判定啟動超時(shí)服務(wù)起不來。一個(gè)成熟的客戶端工程會把這個(gè)流程放在盡量靠前的位置避免在入口函數(shù)里做太多耗時(shí)初始化。二是服務(wù)在Vista之后跟用戶桌面不在同一個(gè)會話里也就是所謂的會話0隔離所以服務(wù)不能直接彈窗給用戶看必須通過一個(gè)用戶態(tài)進(jìn)程再轉(zhuǎn)一道。這也是為什么終端安全產(chǎn)品通常都是“服務(wù)托盤程序”兩個(gè)進(jìn)程配合服務(wù)管業(yè)務(wù)托盤管交互兩者通過IPC通信。如果你在準(zhǔn)備這類崗位我強(qiáng)烈建議你用vs2015手寫一個(gè)最小的Windows服務(wù)真實(shí)跑一遍安裝、啟動、停止、卸載的完整流程再試試在服務(wù)里創(chuàng)建一個(gè)線程做定時(shí)任務(wù)、把日志寫到指定文件。這一套做完你對服務(wù)控制管理器、服務(wù)狀態(tài)機(jī)、會話隔離的理解就遠(yuǎn)比背概念扎實(shí)。3.2 “輸入驗(yàn)證路徑遍歷”安全編碼意識是必選項(xiàng)熱搜詞里這個(gè)“輸入驗(yàn)證路徑遍歷”非常能說明問題。雖然路徑遍歷Path Traversal這個(gè)詞在Web漏洞里更常見但它在客戶端開發(fā)里也一樣存在而且安全廠商自己的客戶端要是出現(xiàn)這類漏洞影響面會被放大很多倍。路徑遍歷的本質(zhì)是什么就是程序拿到外部輸入文件名、路徑字符串、壓縮包內(nèi)的條目路徑、服務(wù)器下發(fā)的策略路徑?jīng)]有做校驗(yàn)就拼到了文件系統(tǒng)操作里。攻擊者構(gòu)造..\\..\\Windows\\system32\\xxx這樣的路徑就能讓程序去讀寫設(shè)計(jì)者本不想讓它碰的位置。Windows下面的路徑規(guī)則和Unix有差異這導(dǎo)致過濾變得更麻煩Windows的文件系統(tǒng)不區(qū)分大小寫、同時(shí)接受/和\作為分隔符、還存在短文件名8.3名、Unicode等價(jià)字符、NTFS備用數(shù)據(jù)流等一堆容易被忽略的旁路。修復(fù)思路倒不復(fù)雜但要求開發(fā)者有那個(gè)意識拿到外部路徑先做規(guī)范化用PathCchCanonicalize之類的函數(shù)把路徑轉(zhuǎn)成標(biāo)準(zhǔn)形式然后校驗(yàn)規(guī)范化后的結(jié)果是否仍然位于允許的根目錄之下而不是簡單地用黑名單去匹配..。奇安信自己就有代碼衛(wèi)士這種做靜態(tài)代碼掃描的產(chǎn)品他們比誰都清楚這類漏洞有多普遍。所以筆試?yán)锍鲞@個(gè)考點(diǎn)本質(zhì)上是在考察候選人有沒有“所有外部輸入都不可信”的安全編碼習(xí)慣。3.3 “天擎卸載”相關(guān)搜索背后的自我保護(hù)機(jī)制搜索熱詞里關(guān)于“天擎卸載”的討論特別多這本身就從側(cè)面反映了一件事這類終端安全產(chǎn)品的自我保護(hù)機(jī)制做得很重不是隨手就能卸掉的。站在普通用戶的角度確實(shí)會覺得煩但站在客戶端開發(fā)的角度這里的每一個(gè)“煩”背后都是一套技術(shù)設(shè)計(jì)。終端安全軟件為什么要拼命保護(hù)自己因?yàn)閻阂廛浖诠裟愕陌踩浖耙欢〞葒L試干掉這個(gè)“看門狗”。如果病毒能通過結(jié)束進(jìn)程、刪除文件、停掉服務(wù)的方式把殺毒軟件廢掉那整個(gè)防線就沒意義了。所以安全客戶端常見的自我保護(hù)手段包括但不限于雙進(jìn)程互相守護(hù)一個(gè)被殺另一個(gè)立刻拉起來服務(wù)配置失敗自動重啟SCM層面就有自愈能力對關(guān)鍵文件、注冊表鍵、進(jìn)程對象設(shè)置DACL拒絕非授權(quán)用戶的刪除、修改和終止操作利用內(nèi)核驅(qū)動做進(jìn)程保護(hù)讓用戶態(tài)無法直接結(jié)束受保護(hù)進(jìn)程卸載流程需要驗(yàn)證碼、密碼或后臺策略審批。我特意強(qiáng)調(diào)一下這里講的是原理和設(shè)計(jì)思路而不是教人怎么繞過卸載。作為一個(gè)候選人你如果能在面試?yán)锇堰@些自我保護(hù)機(jī)制講清楚并說明白“為什么這么設(shè)計(jì)會犧牲一部分用戶體驗(yàn)但這是安全產(chǎn)品必須付出的代價(jià)”面試官會認(rèn)為你對業(yè)務(wù)場景有真實(shí)理解而不是只會背API。4. 如果讓我重寫這套筆試題我會重點(diǎn)押這四個(gè)方向前面聊的是崗位背景和技術(shù)棧這一節(jié)進(jìn)入實(shí)戰(zhàn)。如果讓我站在出題人的角度或者用今天的話說“重新梳理一套終端安全客戶端筆試題的重點(diǎn)”我覺得大致會落在下面這四個(gè)方向上。4.1 線程同步進(jìn)程守護(hù)和多線程日志都要用它線程同步幾乎是Windows客戶端筆試必出的考點(diǎn)因?yàn)榘踩浖锏教幎际嵌嗑€程。以一個(gè)進(jìn)程守護(hù)模塊為例監(jiān)控線程每隔幾秒檢查被保護(hù)進(jìn)程是否存活如果死了就拉起業(yè)務(wù)線程不停地往共享隊(duì)列里寫入新的告警還有一個(gè)線程專門從隊(duì)列里取數(shù)據(jù)上傳。這里至少有三種同步對象可以考隊(duì)列用臨界區(qū)還是互斥量保護(hù)怎么通知等待線程有數(shù)據(jù)了多個(gè)工作線程怎么限制并發(fā)數(shù)量我拿生活場景做個(gè)類比臨界區(qū)就像麥當(dāng)勞的取餐口同一時(shí)間只能有一個(gè)人取餐但大家都堵在窗口前排隊(duì)互斥量就像公共廁所的門鎖一個(gè)人進(jìn)去把門鎖上其他人等而且這個(gè)鎖可以跨樓層跨進(jìn)程事件就像鬧鐘你設(shè)定好了到點(diǎn)叫人直接通知等待它的人“活干完了”信號量就像停車場入口的剩余車位計(jì)數(shù)每進(jìn)一輛車減一車滿了后面就得等。筆試中經(jīng)常追問的還有死鎖。產(chǎn)生死鎖的四個(gè)條件——互斥、持有并等待、不可剝奪、循環(huán)等待——背出來只是第一步更重要的是你能不能在一個(gè)具體的場景里指出為什么會出現(xiàn)循環(huán)等待以及怎么用鎖的排序或超時(shí)機(jī)制解掉它。我在實(shí)際項(xiàng)目里見過最典型的死鎖就是A線程持鎖1去要鎖2B線程持鎖2去要鎖1兩邊都卡死。解決方式要么是全局定一個(gè)加鎖順序要么是WaitForSingleObject時(shí)給超時(shí)時(shí)間拿不到鎖就主動退出再重試。這類問題面試官非常愛深挖因?yàn)槟芸闯鰜砗蜻x人有沒有真的處理過線上問題。4.2 共享內(nèi)存與IPC客戶端各模塊之間怎么對話安全軟件的客戶端不是單一進(jìn)程前面說過它有服務(wù)、有托盤界面、有各業(yè)務(wù)模塊。這些進(jìn)程之間怎么通信就是一個(gè)高頻考點(diǎn)。剪貼板肯定不行那是給人用的Socket又太重在本地通信里還要走協(xié)議棧沒必要。最常見的方案是命名管道、共享內(nèi)存加事件通知以及具名互斥量做一些簡單的單實(shí)例控制。其中共享內(nèi)存很適合大批量數(shù)據(jù)傳輸?shù)膱鼍氨热鐠呙枰姘呀Y(jié)果列表全部傳給界面層用命名管道一條條傳效率太低了。典型做法是創(chuàng)建一個(gè)共享內(nèi)存文件映射對象服務(wù)端往里寫數(shù)據(jù)同時(shí)創(chuàng)建一個(gè)事件對象寫完以后發(fā)信號客戶端等待事件并讀取共享內(nèi)存里的內(nèi)容。這里面試官很可能追問多個(gè)客戶端同時(shí)讀怎么辦共享內(nèi)存的大小怎么定如果寫了一半進(jìn)程崩潰了另一端怎么發(fā)現(xiàn)還有一個(gè)點(diǎn)容易被忽略安全客戶端和驅(qū)動之間的通信。這通常不是用共享內(nèi)存而是要往驅(qū)動發(fā)IOCTL通過DeviceIoControl調(diào)用驅(qū)動暴露的設(shè)備接口。這個(gè)鏈路已經(jīng)超出純用戶態(tài)筆試范圍了但很多崗位JD里會寫著“有驅(qū)動開發(fā)經(jīng)驗(yàn)優(yōu)先”。你哪怕沒有實(shí)際寫過驅(qū)動也要知道IOCTL的基本交互模型不然面試聊到“底層攔截”的時(shí)候會卡殼。4.3 DLL注入與API鉤子分得清IAT Hook和Inline Hook嗎終端安全軟件自己要監(jiān)測別的進(jìn)程手段通常就是注入和鉤子。雖然今天很多EDR產(chǎn)品已經(jīng)盡量把采集能力下沉到驅(qū)動層但用戶態(tài)的DLL注入和API Hook依然是繞不開的知識點(diǎn)。筆試不太會讓你憑空設(shè)計(jì)一個(gè)免殺方案但會問基礎(chǔ)原理以及讓你評價(jià)幾種注入方式各自的利弊。常見的用戶態(tài)DLL注入方式包括SetWindowsHookEx注入依賴窗口消息機(jī)制無窗口進(jìn)程注入不了、CreateRemoteThreadLoadLibrary在目標(biāo)進(jìn)程里創(chuàng)建遠(yuǎn)程線程加載DLL最經(jīng)典但也很容易被安全軟件攔截、QueueUserAPC注入目標(biāo)線程必須處于可告警狀態(tài)、AppInit_DLLs注冊表項(xiàng)開機(jī)時(shí)加載到加載user32.dll的進(jìn)程里兼容性差且影響面大。2020年那個(gè)時(shí)間點(diǎn)上微軟對AppInit_DLLs的限制已經(jīng)挺嚴(yán)格了所以實(shí)際產(chǎn)品用得越來越少。然后就是Hook。IAT Hook和Inline Hook的區(qū)別一定要講清楚。IAT Hook是修改PE文件導(dǎo)入地址表讓目標(biāo)API調(diào)用跳轉(zhuǎn)到自己的函數(shù)實(shí)現(xiàn)簡單、穩(wěn)定性高但只能攔截通過導(dǎo)入表調(diào)用的API如果目標(biāo)程序通過LoadLibraryGetProcAddress動態(tài)獲取函數(shù)地址再調(diào)用IAT Hook就攔不到。Inline Hook更狠直接修改目標(biāo)函數(shù)開頭的機(jī)器碼跳轉(zhuǎn)到自己的代碼里能攔得更徹底但問題也更多要處理指令長度對齊問題要處理多線程同時(shí)調(diào)用時(shí)的競態(tài)還要注意被Hook的系統(tǒng)函數(shù)可能有熱補(bǔ)丁機(jī)制。一個(gè)筆試?yán)锍3霈F(xiàn)的問題是“Inline Hook為什么有可能導(dǎo)致目標(biāo)程序崩潰”答案就在于你劈開的那幾個(gè)字節(jié)是不是一條完整指令邊界。4.4 內(nèi)存管理與崩潰排查客戶端要能在復(fù)雜環(huán)境活下去安全軟件最怕什么不是被病毒干死而是自己崩了。普通軟件崩了大不了重啟一下安全軟件如果經(jīng)常崩潰用戶第一時(shí)間就會把它卸了。所以說客戶端開發(fā)工程師必須會排查崩潰問題這也是筆試和面試?yán)锝?jīng)常被包裝成場景題的一塊。Windows上排查崩潰的標(biāo)準(zhǔn)鏈路是等崩潰發(fā)生用LocalDumps注冊表項(xiàng)配置讓系統(tǒng)自動生成dump然后用Windbg打開dump文件執(zhí)行!analyze -v查看分析結(jié)果關(guān)鍵是找出出錯線程的?;厮莺彤惓顟B(tài)。這個(gè)過程里有兩個(gè)非常實(shí)操的細(xì)節(jié)一是配置LocalDumps時(shí)要寫成64位和32位兩套取決于目標(biāo)進(jìn)程是64位還是32位二是分析dump時(shí)必須保證exe和pdb版本跟崩潰現(xiàn)場的版本一致否則棧是還原不出來的。如果把這道題延伸成面試場景面試官會問“如果崩潰只在用戶機(jī)器上出現(xiàn)你自己機(jī)器復(fù)現(xiàn)不了怎么處理”這時(shí)候你如果能說出“上線前給客戶端開好崩潰轉(zhuǎn)儲策略拿到用戶側(cè)dump檢查模塊版本和符號用Windbg分析出崩潰?!本鸵呀?jīng)比絕大多數(shù)候選人強(qiáng)了。再往深一點(diǎn)還會追問內(nèi)存損壞的常見原因比如緩沖區(qū)越界、懸空指針、重復(fù)釋放以及C里怎么防止這些問題——RAII、智能指針、邊界檢查、不要裸new/delete。這些習(xí)慣在安全軟件的長期維護(hù)中非常重要。5. Windows客戶端開發(fā)求職者的備考路線與避坑建議5.1 從API調(diào)用層面往系統(tǒng)機(jī)制層面去理解準(zhǔn)備這類崗位最大的忌諱就是只背API。CreateFile、ReadFile、WriteFile這些API誰都會調(diào)但你得理解它們背后的內(nèi)核對象、句柄表、用戶態(tài)到內(nèi)核態(tài)的切換開銷、緩沖區(qū)是怎么拷貝的。要養(yǎng)成一種習(xí)慣每用一個(gè)系統(tǒng)API就想想它的內(nèi)部機(jī)制大概是什么樣瓶頸在哪為什么需要傳入這個(gè)參數(shù)。比如調(diào)用WaitForSingleObject時(shí)為什么核心對象就分“已通知”和“未通知”兩個(gè)狀態(tài)比如打開注冊表鍵為什么最好用RegOpenKeyEx而不是RegOpenKey。這些理解比多背二十個(gè)API更有價(jià)值。Windbg的使用建議盡早學(xué)起來。不是要你成為內(nèi)核調(diào)試專家而是至少會用!analyze -v、dt、dps這些命令能看清一個(gè)崩潰現(xiàn)場的調(diào)用棧。Windows客戶端開發(fā)崗位調(diào)試能力幾乎可以直接等同于工程能力面試聊深了很容易聊到“你之前是怎么定位一個(gè)線上崩潰的”這時(shí)候沒有實(shí)戰(zhàn)經(jīng)驗(yàn)是編不出來的。5.2 動手做一個(gè)小而完整的終端安全Demo比刷一百道題有效如果時(shí)間允許我特別建議你在準(zhǔn)備期間動手寫一個(gè)“迷你版終端安全客戶端”。范圍不需要大具體可以做這幾層一個(gè)能通過SCM安裝啟動的Windows服務(wù)服務(wù)里創(chuàng)建一個(gè)線程做定時(shí)掃描比如掃描某個(gè)目錄下是否新增了指定擴(kuò)展名的文件檢測到異常時(shí)通過命名管道把一個(gè)告警消息發(fā)給托盤程序托盤程序用一個(gè)簡單窗口顯示告警列表并且提供一鍵退出按鈕但退出時(shí)必須先向服務(wù)驗(yàn)證一個(gè)“授權(quán)碼”而不是直接結(jié)束進(jìn)程。這個(gè)Demo麻雀雖小但幾乎覆蓋了前面說的所有考點(diǎn)服務(wù)編程、線程同步、進(jìn)程間通信、消息循環(huán)、安全校驗(yàn)。做完之后你手上有真實(shí)的代碼可以講、有真實(shí)的踩坑經(jīng)歷可以聊比抽象地背一堆概念要有說服力得多。我見過很多候選人筆試成績不錯但一聊項(xiàng)目就露怯根本原因就是沒有親手跑通過一條完整的系統(tǒng)鏈路。5.3 別把精力浪費(fèi)在過度追逐新框架上2020年那會兒很多準(zhǔn)備客戶端崗位的同學(xué)都在糾結(jié)要不要學(xué)Electron、Flutter、乃至Rust。我給的答復(fù)一直是如果目標(biāo)是安全廠商的Windows客戶端開發(fā)崗重點(diǎn)一定放在C、Win32、系統(tǒng)機(jī)制、調(diào)試和排查能力上。新框架年年有但底層技術(shù)永遠(yuǎn)是最穩(wěn)的護(hù)城河。一個(gè)能把Win32、服務(wù)、Hook、注入、崩潰分析講得頭頭是道的人學(xué)什么新框架都快反過來只會在框架里做界面的人遇到系統(tǒng)級問題就抓瞎。當(dāng)然這里也不是說新東西完全不用看。像新版本的Visual Studio工程配置、C20范圍庫的用法、Windows 10以后引入的部分新特性都可以當(dāng)作錦上添花了解。但主次關(guān)系一定要擺對別本末倒置。5.4 我記得的幾個(gè)高頻失誤你對照一下最后分享幾個(gè)我在實(shí)際面試?yán)锓磸?fù)見到的高頻失誤你備考的時(shí)候可以專門對照檢查失誤類型具體表現(xiàn)應(yīng)對思路不檢查API返回值調(diào)完CreateFile不判斷句柄是否有效直接往下走養(yǎng)成每調(diào)一次關(guān)鍵API就檢查返回值和GetLastError的習(xí)慣忽略UNICODE字符集工程還是多字節(jié)字符集遇到中文路徑就崩新項(xiàng)目一律UNICODE字符串處處用TCHAR對應(yīng)宏或直接wchar_t混淆DLL安全細(xì)節(jié)不知道LoadLibrary搜索順序、不了解KnownDLLs理解DLL搜索路徑排序?qū)懘a時(shí)盡量用絕對路徑加載對崩潰dump無從下手程序崩了就加日志、加打印而不是用調(diào)試器分析學(xué)會配置LocalDumps學(xué)會用Windbg還原現(xiàn)場線程同步意識薄弱共享數(shù)據(jù)不加鎖總以為“大概不會同時(shí)訪問”所有跨線程訪問的變量先問自己是否加鎖我在實(shí)際項(xiàng)目中看到過太多次因小失大一個(gè)DWORD共享變量沒加volatile或者沒加鎖線上偶發(fā)數(shù)據(jù)錯亂排查了兩天最后發(fā)現(xiàn)就是線程同步問題。安全客戶端的運(yùn)行環(huán)境極其復(fù)雜用戶的機(jī)器上可能同時(shí)跑著多家安全產(chǎn)品、各種插件、老舊的第三方驅(qū)動任何一個(gè)不起眼的錯誤都可能被放大成難以復(fù)現(xiàn)的線上故障。所以你如果真心想在這個(gè)方向做長久一定要從一開始就養(yǎng)成嚴(yán)謹(jǐn)?shù)南到y(tǒng)級編程習(xí)慣。關(guān)于這類筆試的內(nèi)容一篇還講不完。線程同步的具體場景題、IOCTL通信的完整流程、dump分析的實(shí)戰(zhàn)演示這些都可以單獨(dú)拆開寫成一篇。如果后面有機(jī)會我再把手頭做過的幾個(gè)真實(shí)排查案例整理出來繼續(xù)往下聊。