成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

用c++解析PE 繞過AV/EDR API掛鉤

企業動態
這篇文章涵蓋了幾個主題,比如系統調用、用戶模式與內核模式,以及我在本文將要介紹的Windows體系結構。

[[342999]]

 這篇文章是使用最初是由@spotless編寫的代碼來繞過AV/ EDR創建的API掛鉤。我想說明的是,spotless已經在這方面做了一些準備工作,我只是做了一些小的功能更改,并添加了許多注釋和文檔。這主要是為了提高我對這個主題的理解,因為我發現在手頭有MSDN文檔的情況下逐個函數地瀏覽代碼是了解它如何工作的好方法。它可能有點單調乏味,這就是為什么我對代碼進行過多的文檔化,以便其他人能夠從中吸取經驗。

這篇文章涵蓋了幾個主題,比如系統調用、用戶模式與內核模式,以及我在本文將要介紹的Windows體系結構。在這篇文章中,我將在本文中假定對這些主題有一定程度的了解,這篇文章的代碼可以在這里找到。

理解API掛鉤

鉤到底是什么?它是AV/EDR產品常用的一種技術,用于攔截函數調用,并將代碼執行流程重定向到AV/EDR,以檢查調用并確定是否為惡意調用。這是一項功能強大的技術,因為防御性應用程序可以一步一步查看你進行的每個函數調用,確定其是否為惡意程序并將其阻止。更糟糕的是(對于攻擊者來說),這些產品在系統庫/ DLL中掛鉤本地函數,這些DLL位于傳統使用的Win32 API之下。例如,WriteProcessMemory是一種常用的Win32 API,用于將shellcode寫入進程地址空間,實際上調用了ntdll.dll中包含的未文檔化的本機函數NtWriteVirtualMemory。 NtWriteVirtualMemory實際上是對內核模式的系統調用的包裝函數。由于AV / EDR產品能夠在用戶模式代碼可訪問的最低級別上掛接函數調用,因此無法對其進行轉義。

掛鉤發生的位置

為了理解如何繞過掛鉤,我們需要知道它們是如何以及在哪里創建的。當進程啟動時,某些庫或DLL將作為模塊加載到進程地址空間中。每個應用程序都是不同的,將加載不同的庫,但無論它們的功能如何,實際上所有的應用程序都將使用ntdll.dll,因為許多最常見的Windows函數都駐留在其中。防御性產品通過在DLL中連接函數調用來利用這一事實。通過掛鉤,我們實際上是指修改函數的匯編指令,在函數的開頭插入一個無條件跳轉到EDR的代碼中。EDR處理函數調用,如果允許,執行流將跳回原始函數調用,以便函數正常執行,而調用進程不知情。

識別掛鉤

所以我們知道在我們的進程中,ntdll.dll模塊已經被修改,我們不能相信任何使用它的函數調用。我們怎樣才能解開這些掛鉤呢?我們可以確定我們所使用的Windows的確切版本,找出實際的組裝說明應該是什么,并嘗試在運行中修補它們。但是這樣做會很乏味,容易出錯,而且不可重用。事實證明,磁盤上已經存在一個原始的,未經修改的,未經摘錄的ntdll.dll版本!

因此,正確的策略應該如下。首先,我們將ntdll.dll的副本映射到我們的進程內存中,以使用一個干凈的版本。然后,我們將在過程中確定掛鉤版本的位置。最后,我們只需用干凈的代碼重寫掛鉤的代碼,就可以了!

映射NtDLL.dll

映射ntdll.dll文件的視圖實際上非常簡單,我們獲得了ntdll.dll的句柄,獲得了它的文件映射的句柄,并將其映射到我們的進程中:

  1. HANDLE hNtdllFile = CreateFileA("c:\\windows\\system32\\ntdll.dll", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);HANDLE hNtdllFileMapping = CreateFileMapping(hNtdllFile, NULL, PAGE_READONLY | SEC_IMAGE, 1, 0, NULL);LPVOID ntdllMappingAddress = MapViewOfFile(hNtdllFileMapping, FILE_MAP_READ, 0, 0, 0); 

很簡單,現在我們已經將干凈的DLL映射到我們的地址空間中,現在我們來查找掛鉤副本。

要在進程內存中找到掛鉤的ntdll.dll的位置,我們需要在進程中加載的模塊列表中找到它。本例中的模塊是DLL和進程的主要可執行文件,在進程環境塊中存儲了它們的列表。PEB的具體介紹請點擊這里。要訪問這個列表,我們可以獲取流程和所需模塊的句柄,然后調用GetModuleInformation。然后,我們可以從miModuleInfo結構中檢索DLL的基地址:

  1. handle hCurrentProcess = GetCurrentProcess(); 
  2. HMODULE hNtdllModule = GetModuleHandleA("ntdll.dll"); 
  3. MODULEINFO miModuleInfo = {}; 
  4. GetModuleInformation(hCurrentProcess, hNtdllModule, &miModuleInfo, sizeof(miModuleInfo)); 
  5. LPVOID pHookedNtdllBaseAddress = (LPVOID)miModuleInfo.lpBaseOfDll; 

好的,因此我們在進程中具有已加載的ntdll.dll模塊的基地址。但這到底是什么意思?DLL是一種與EXE一起可移植的可執行文件。這意味著它是一個可執行文件,因此包含各種不同類型的標頭文件和節,這些文件可讓操作系統知道如何加載和執行該文件。如上所示PE標頭是密集而復雜的,但是我發現看到一個實際的工作示例僅利用了其中的一部分,就很容易理解。哦,圖片也不會受傷。那里有很多細節級別各不相同的東西,但是來自Wikipedia的一個很好的示例有足夠的細節而又不至于太令人費解:

 

你可以在DOS標頭的PE開頭看到Windows的遺留物,它一直都在那兒,但現在已經沒有什么用處了。但是,我們將獲取其地址,作為獲取實際PE標頭的偏移量:

  1. PIMAGE_DOS_HEADER hookedDosHeader = (PIMAGE_DOS_HEADER)pHookedNtdllBaseAddress; 
  2. PIMAGE_NT_HEADERS hookedNtHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)pHookedNtdllBaseAddress + hookedDosHeader->e_lfanew); 

在這里,hookedDosHeader結構體的e_lfanew字段包含一個到模塊內存的偏移量,該偏移量標識PE標頭文件實際上從哪里開始,也就是上圖中的COFF頭文件。

現在我們位于PE標頭的開頭,我們可以開始對其進行解析以查找所需的內容。但是,讓我們退后一步,準確地確定我們在尋找什么,這樣我們就知道什么時候我們找到了它。

每個可執行文件/ PE都有許多部分,這些部分代表程序中各種類型的數據和代碼,例如實際的可執行代碼、資源、圖像、圖標等。這些類型的數據在可執行文件中分為不同的帶標簽的部分,命名為.text、.data、.rdata和.rsrc。.text節(有時也稱為.code節)是緊隨其后的,因為它包含組成ntdll.dll的匯編語言指令。

那么我們如何訪問這些部分呢?在上圖中,我們看到一個節表,其中包含一個指向每個節開始的指針的數組。非常適合遍歷和查找每個部分,這是通過使用for循環并遍歷掛鉤edNtHeader-> FileHeader.NumberOfSections字段的每個值來找到.text部分的方法:

  1. for (WORD i = 0; i < hookedNtHeader->FileHeader.NumberOfSections; i++) 
  2.     // loop through each section offset 

從現在開始,別忘了我們將在循環中尋找.text部分。為了識別它,我們使用循環計數器i作為節表本身的索引,并獲得指向節頭的指針

  1. PIMAGE_SECTION_HEADER hookedSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD_PTR)IMAGE_FIRST_SECTION(hookedNtHeader) + ((DWORD_PTR)IMAGE_SIZEOF_SECTION_HEADER * i)); 

每個節的節標題包含該節的名稱,因此,我們可以查看每一個,看看它們是否與.text匹配:

  1. if (!strcmp((char*)hookedSectionHeader->Name, (char*)".text")) 
  2.     // process the header 

無論如何它的標頭如何,我們找到了.text節!現在我們需要知道該部分中實際代碼的大小和位置。本節標頭包含了以下兩方面內容:

  1. LPVOID hookedVirtualAddressStart = (LPVOID)((DWORD_PTR)pHookedNtdllBaseAddress + (DWORD_PTR)hookedSectionHeader->VirtualAddress); 
  2. SIZE_T hookedVirtualAddressSize = hookedSectionHeader->Misc.VirtualSize; 

現在,我們有了所有需要的東西,我們可以用磁盤上的干凈的ntdll.dll重寫加載和鉤住的ntdll.dll模塊的.text部分:

· 要復制的源文件(磁盤上的內存映射文件ntdll.dll);要復制到的目的地(.text節的hookedSectionHeader->VirtualAddress);

· 復制的字節數(hookedSectionHeader->Misc.VirtualSize字節)。

保存的輸出

至此,我們保存了.text節的全部內容,因此我們可以對其進行檢查,并將其與干凈版本進行比較,從而知道解除鏈接成功了:

  1. char* hookedBytes{ new char[hookedVirtualAddressSize] {} }; 
  2. memcpy_s(hookedBytes, hookedVirtualAddressSize, hookedVirtualAddressStart, hookedVirtualAddressSize); 
  3. saveBytes(hookedBytes, "hooked.txt", hookedVirtualAddressSize) 

這僅是掛鉤.text節的一個副本,并調用saveBytes函數,該函數將字節寫入一個名為hook .txt的文本文件,稍后我們將研究這個文件。

內存管理

為了重寫.text部分的內容,我們需要保存當前的內存保護并將其更改為讀/寫/執行,完成后,我們將其改回來

  1. bool isProtected; 
  2. isProtected = VirtualProtect(hookedVirtualAddressStart, hookedVirtualAddressSize, PAGE_EXECUTE_READWRITE, &oldProtection); 
  3. // overwrite the .text section here 
  4. isProtected = VirtualProtect(hookedVirtualAddressStart, hookedVirtualAddressSize, oldProtection, &oldProtection); 

繞過過程

我們終于到了繞過過程,首先,我們從獲取內存映射的ntdll.dll的開頭地址開始,作為我們的復制源:

  1. LPVOID cleanVirtualAddressStart = (LPVOID)((DWORD_PTR)ntdllMappingAddress + (DWORD_PTR)hookedSectionHeader->VirtualAddress); 

我們還要保存這些字節,以便稍后進行比較:

  1. char* cleanBytes{ new char[hookedVirtualAddressSize] {} }; 
  2. memcpy_s(cleanBytes, hookedVirtualAddressSize, cleanVirtualAddressStart, hookedVirtualAddressSize); 
  3. saveBytes(cleanBytes, "clean.txt", hookedVirtualAddressSize); 

現在我們可以用未鉤住的ntdll.dll重寫.text部分:

  1. memcpy_s(hookedVirtualAddressStart, hookedVirtualAddressSize, cleanVirtualAddressStart, hookedVirtualAddressSize); 

怎么知道是否被繞過了?

那么我們怎么知道我們實際上刪除了掛鉤,而不是移動了一堆字節呢?讓我們檢查一下輸出文件hook .txt和clean.txt。這里我們使用VBinDiff對它們進行比較,第一個示例是在沒有安裝AV/EDR產品的測試設備上運行程序,正如預期的那樣,加載的ntdll和磁盤上的ntdll是相同的:

因此,讓我們再次在運行有掛鉤的Avast Free Antivirus的計算機上再次運行它:

 

 

現在,讓我們看看hooked.txt的開頭和clean.txt的結尾,它們之間有明顯的區別,用紅色標出。我們可以獲取這些原始字節,這些原始字節實際上代表匯編指令,然后使用在線反匯編程序將它們轉換為其匯編表示。

以下就是干凈的ntdll.dll的反匯編結果:

  1. mov    QWORD PTR [rsp+0x20],r9 
  2. mov    QWORD PTR [rsp+0x10],rdx 

以下就是掛鉤后的版本:

  1. jmp    0xffffffffc005b978 
  2. int3 
  3. int3 
  4. int3 
  5. int3 
  6. int3 

可以看到一個清晰的jump! ,這意味著當它被加載到我們的進程中時,ntdll.dll中的某些內容已經發生了明顯的變化。

但是我們怎么知道它實際上是在連接一個函數調用呢?讓我們看看能不能找到更多的答案。這是頂部掛鉤的DLL和底部干凈的DLL之間的另一個差異示例:

首先清理DLL:

  1. mov    r10,rcx 
  2. mov    eax,0x37 
  3. mov    r10,rcx 
  4. mov    eax,0x3a 

掛鉤的DLL:

  1. jmp    0xffffffffbffe5318 
  2. int3 
  3. int3 
  4. int3 
  5. jmp    0xffffffffbffe4cb8 
  6. int3 
  7. int3 
  8. int3 

現在,我們看到了更多的跳躍。但是這些mov eax和編號指令是什么意思?這些是系統調用號碼!如果你閱讀了我以前的文章,我將介紹如何以及為什么在匯編中準確找到這些內容。這個想法是使用syscall號直接調用底層函數,以避免掛鉤!但是,如果你想運行尚未編寫的代碼怎么辦?如何防止這些掛鉤捕獲你無法更改的代碼?如果你到目前為止已經做到了,那么你已經知道了!因此,讓我們使用Mateusz“j00ru”Jurczyk的簡化版Windows系統調用表,并將syscall編號與其相應的函數調用進行匹配。

看看,我們發現了什么?0x37是NtOpenSection, 0x3a是NtWriteVirtualMemory! ,Avast 顯然是在連接這些函數調用,而且我們知道我們已經用干凈的DLL重寫了它們。

本文翻譯自:https://www.solomonsklash.io/pe-parsing-defeating-hooking.html如若轉載,請注明原文地址:

 

責任編輯:姜華 來源: 嘶吼網
相關推薦

2024-01-04 11:48:32

EDRHook堆棧

2017-11-06 05:52:52

2012-08-03 08:57:37

C++

2023-10-30 10:29:50

C++最小二乘法

2011-04-11 09:43:25

C++C

2024-07-26 17:59:23

2024-10-16 16:36:22

2025-03-07 10:28:30

2010-01-25 18:24:11

C++

2010-01-27 10:22:53

C++基類

2010-01-21 11:23:58

C++函數調用

2010-01-15 17:38:37

C++語言

2024-12-13 15:40:54

2013-06-24 15:32:00

c++GCC

2010-05-14 15:23:03

2010-01-28 13:15:43

C++參數

2010-02-01 16:40:14

C++枚舉子

2010-02-05 12:57:20

C++ kdevelo

2023-11-09 23:31:02

C++函數調用
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 一区在线视频 | 国产精品一区二区三区在线 | 日韩av免费在线观看 | 3p视频在线观看 | 九九热这里| wwwww在线观看 | 国产操操操 | 天天弄天天操 | 国产日韩精品一区二区 | 亚洲欧美在线视频 | 四虎在线观看 | 精品av | 国产成人精品一区二区三区视频 | 久久夜视频 | 日韩精品一区二区三区免费视频 | 午夜视频一区二区 | 欧美精品成人一区二区三区四区 | 亚洲成人精 | 精品一区二区三区免费毛片 | 午夜影院在线观看 | 日韩www| 丝袜一区二区三区 | 人人做人人澡人人爽欧美 | 久久久久久久电影 | 欧洲精品久久久久毛片完整版 | 影音先锋中文字幕在线观看 | 国产欧美一区二区三区在线看 | 亚洲精品一区二区三区在线 | 久久九精品 | 中文字幕视频一区二区 | 天天草天天爱 | 欧美一级视频免费看 | 久久久91精品国产一区二区精品 | 成人综合视频在线观看 | 国产精品成人一区 | 一级毛片在线播放 | 精品视频成人 | 免费观看视频www | 色婷婷综合久久久中字幕精品久久 | 国产精品第2页 | 精品久久久一区二区 |