Posts

Showing posts with the label PE結構

Windows內核安全之 線程調度

Image
線程一個比較重要的概念是, CPU中永遠調度的是線程, 而不是進程, 而進程的切換完全是源於是否同一個CR3寄存器, 詳見SwapContext的實現 以下先列出KTHREAD對象 內核中對線程優先級(priority) 分別使用0~31表示 實時(Real-time)類別: 16~31 動態(Dynamic)類別: 1~15 系統(System)類別: 0 線程在內核的執行體層(Executable Layer)中 使用以下6種優先級 實時(Realtime) 高(High) 普通之上(Above normal) 普通(normal) 普通之下(Below normal) 低(Low) 執行體層透過一個數組PspPriorityTable 轉換成真正的表示 以下是他們的對應關係 實時(Realtime) 24 高(High) 13 普通之上(Above normal) 10 普通(normal) 8 普通之下(Below normal) 6 低(Low) 4 進程的對象EPROCESS結構中 BasePriority 指定一個進程中所有線程的預設優先級 可以通過KeSetPriorityAndQuantumProcess 或 KeSetBasePriorityThread 函數設置優先級 KTHREAD中的Priority域記錄一個線程當前實際優先級, 他一般是在BasePriority基礎上提升到某個增量 Priority不會超過0~15 內核通過調用KiComputeNewPriority計算並調整非實時的線程優先級 我們可以使用NtSetInformationProcess為進程的基礎優先級進行微調, 所以一條線程在執行體中的值並非數組中指定的值 優先級的提升: ------------- 由於每個線程優先級都有對應的鏈表維護著相同優先級的線程 方便調度 所以優先級的提升, 一定是在加入鏈表之前 內核中不小函數的參數使用KPRIORITY Increment進行提升 如: KeInsertQueueApc KePulseEv...

Windows安全之 應用層(Ring3)的內存管理(二) 內存映射文件

Image
2.1 內存映射文件 映射到內存的可執行文件與DLL 載入.exe到地址空間 1. 使用CreateProcess 所指定可執行文件的位置, 如果無法找到.exe 則返回FALSE 2. 如果找到, 系統會為進程創建一個進程內核對象 3. 系統為進程創建4GB私有地址空間 4. 在地址空間裡, 系統預訂(Reserve)一塊足夠大的空間來容納.EXE文件,待預訂的具體位置在x86是0x00400000, 在x64 是動態加載, 5. 系統會為地址空間區域進行標示, 表明該區域 的後備存儲器是在.EXE文件自身, 而非頁交換文件 載入.dll 到地址空間, 使用LoadLibrary函數 載入DLL 1. 系統會預訂一塊足夠大的地址空間區域來容納.dll, 待預訂的地址在.dll中指明, 預設為0x00100000 , 如已加載 , 加載到別的地方時, .DLL內部會進行重定位, 一開始都假設區域的加載地址是0x00100000 , 那以這個地址作為基址, 進行偏移就錯了, 所以需要重新獲取dll 加載地址進行重定位(Relocation), 在我PE文件章中已提及過 2. 系統會對地址空間進行標示, 標示是後備存儲器是來自.DLL文件(硬盤上) ,如果因無法分配dll文件大小使無法加載到原本指定位置(0x00100000), 而系統需要進行重定位, 系統會額外標示有一部份是映射到頁交換文件 2.2 內存不會共享靜態數據 如果一個應用程序已在運行, 我們再打開一個新進程時, 系統會創建4GB內存地址空間及新的進程及線程對象,假設第二個進程現在開始, 這時系統只不過把應用程序的代碼與數據的虛擬內存頁面,映射到第二個實例空間中 如下圖 P.S 這裡的圖為什麼說是虛擬內存, 沒有物理內存, 因為所有虛擬地址空間跟物理內存本來就存在映射關係,所以在內存中他們地址也是一樣的,不用特別複製, 虛擬地址映射到虛擬內存是因為當頁面沒有用時被交換到硬盤(虛擬內存) 是會隨機變動的, 頁面就更新到新進程到。 2.3 頁面寫時複製 但這裡存第另一個問題, 當一個進程修改了頁面內容, 就改變了本來進程初始化的樣貌, 甚至令其他同時運行的程序崩潰. 為解決這個問題, 在寫入頁面時 系統會首先攔截這項嘗試, 把要修改的頁面, 複製到...

Windows安全之PE結構(七)之重定位表(Relocation Table)結構分析

Image
什麼是重定位呢.. 是把我們一些地址,重新定位的意思。 我們都知道每個程序在windows系統中 都有自己4GB的虛擬空間, 可是如果在WIN系統只有.EXE這樣一種PE文件的話。真是沒有重定位的重要性存在,可是事實有動態鏈接庫.DLL檔 , DLL檔本身自己是沒有一個虛擬空間,而他是需要被加載到不同的.EXE的私有空間中, 當加載後 有什麼基址是需要重定位呢? 就是DLL中的一些牽涉到直接尋址的一些指令 就需要進行重定位處理 直接尋址: 簡單意義就是在匯編代碼中 看到有 [XXXXXXX] 的語句 基本上都需要重定位 間接尋址: 這是額外知識, 匯編語言可以把一個地址存入eax 然後透過eax去尋址,這個eax就不需要重定位了 那重定位 究竟需要什麼呢?? 首先一定是需要重定位的地方 然後就是重定位的結果 如下圖 dec dword ptr [100030000] push dword ptr [10003000] mov eax, dword ptr[10003000] 這三個地址是需要進行重定位的,因為這是反匯編.dll檔後的靜態地址,加載後這個地址就不一樣了 假如加載後 dll在exe檔中的虛擬基地址是0x20000000 那0x20000000h - 0x10000000h = 0x10000000 這裡0x20000000是假設加載dll模塊到exe的虛擬空間後,他的基址是0x20000000 他如 預設DLL的基址 相差了10000000H 那我們就需要把dll中所有的直接尋址 後移10000000H 以上面的例子就是 dec dword ptr [200030000] push dword ptr [20003000] mov eax, dword ptr[20003000] 可是我們怎麼知道重定位表有多少個內容需要重定位呢?? 整個.reloc區塊就如下圖 IMAGE_BASE_RELOCATION STRUCT VirtualAddress DWORD ? ;重定位表的rva SizeOfBlock DWORD ? ;重定位表的大小 TypeOffset WORD ? ;每一項佔一個字(兩個byte) IMAGE_BASE...

Windows安全之PE結構(六)之導出表(IDT)結構分析

Image
在這裡來說 要清一下DLL檔(dynamic link library)的基礎認識, 每一個windows擁有4GB的虛擬內存空間, 可是我們發現 有好多好多的函數 如MessageBox等 在一個win系統下 有無限個程序會調用到這個方法 但如果每一個程序也需要在內存中加載一次 不就重覆了嗎? 有一百個程序需要調用MessageBox, 在內存也只需要加載一次User.dll 所有程序在內存中找到他, 然後複製需要的函數到我們的程序裡. 省了很多內存.我們的微軟前輩們都很強大 lol.. 大部份的DLL文件也包含了導出表! (可不是指所有的DLL) 導出表(EXPORT TABLE) 中的主要成分是一個表格,內含函數名稱,輸出序數等,序數是指定DLL某個函數的16位數字,在指向的DLL中是獨一無二的,在此我們不提倡只用序數來索引函數的方法,這樣會給DLL文件帶來維護的問題,例如當DLL文件一旦升級或修改可能導致調用改DLL的程序無法加載到需要的函數 又是DataDirectory(數據目錄表)的第一個成員指向導出表 IMAGE_EXPORT_DIRECTORY STRUCT Characteristics DWORD ? ;未使用,總是定義為0 TimeDateStamp DWORD ? ;文件生成時間 MajorVersion WORD ? ;未使用,總是定義為0 MinorVersion WORD ? ;未使用,總是定義為0 Name DWORD ? ;模塊的真實名稱 Base DWORD ? ;基數,加上序數就是函數地址數組的索引值 NumberOfFunction DWORD ? ; 導出函數的總數量 NumberOfName DWORD ? ; 以名稱方式導出的函數的總數 AddressOfFunction DWORD ? ; 指向輸出函數地址的RVA AddressOfNames DWORD ? ; 指向輸出函數名稱的RVA AddressOfNameOrdinals DWORD ?; 指向輸出函數序號的RVA IMAGE_EXPORT_DIRECTORY ENDS 以下是count.dll 的解析 我只導出了一個函數 嘗試查找它的地址 用LordPE把它打開,...

Windows安全之PE結構(五)之導入表(IAT)結構分析

Image
輸入表結構 回顧一下, 在PE文件頭的IMAGE_OPTIONAL_HEADER 結構中的DataDirectory(數據目錄表)的第二個成員就是指向輸入表的, 而輸入表是以一個IMAGE_IMPORT_DESCRIPTOR(簡稱IID)的數組開始. 每個PE文件鏈接進來的DLL文件都分別對應一個IID數組結構在這個IID數組中,並沒有指出有多少個項(就是沒有 IMAGE_IMPORT_DESCRIPTOR STRUCT UNION Characteristics DWORD ? OriginalFirstThunk DWORD ? ;一個指針 指向INT基址 (IMAGE_THUNK_DATA數組) ends TimeDataStamp DWORD ? ForwarderChain DWORD ? Name DWORD ? FirstThunk DWORD ? ;一個指針 指向IAT基址 (IMAGE_THUNK_DATA數組) IMAGE_IMPORT_DESCRIPTOR ENDS 以下 IMAGE_THUNK_DATA 這個結構十分重要 WINDOW能把靜態和動態的內存分配達至最小的變動因為UNION是共享內存的 最高位為1時 表示函數以序號方式導入,低31位被看作為一個函數序號 最高位為0時 表示函數以字符串類型的函數名方式導入,這時雙字的值是一個RVA,指向一個IMAGE_IMPORT_BY_NAME的結構中。 IMAGE_THUNK_DATA STRUCT union u1 ForwarderString DWORD ? ;指向一個轉向者字符串的相對地址(RVA) Function DWORD ? ;被輸入的函數的內存地址 Ordinal DWORD ? ;被輸入的API函數值 AddressOfData DWORD ? ;指向IMAGE_IMPORT_BY_NAME ends IMAGE_THUNK_DATA ENDS OriginalFirstThunk 和 FirstThunk (IAT/INT)在硬盤靜態時都是指向同一個地方 就是IMAGE_IMPORT_BY_NAME IMAGE_IMPORT_BY_NAME STRUCT Hint WORD ? ;表示函數的序號 Name BYTE ? ;Name 字段定義了...

Windows安全之PE結構(四) 之 導入表(IAT)與導入函數

Image
來到這一章 , 開始進入人們熟悉的IAT, 一般從事安全的程序員都一定聽過IAT HOOK , EAT HOOK等等, 這些其實都是把PE文件中的一些表格的函數表,透過各類的形式給監聽了 可是我們要去HOOK一個表的函數, 我們一定要懂得他是什麼 我們才可以動手 , 不然很容易就崩潰了. 四 --------------------------------------------------------------------------------------- 很多人認為 學PE結構十分費時, 可是假設你很想深入了解windows系統的編程,內核等知識,你一定需要 了解PE結構, 很多人問我 學這沒有結果,卻不是的, 很多病毒或防毒程序 也完全建基於PE結構的流程上 例如分頁內存注入病毒代碼 把之前說過的pe頭指向 分頁內存的00000000 隙中 廢話不多說 說第四章的重點 - 輸入表 又稱導入表 首先把之前的知識重新來一下總結, 然後再繼續下去講 PE文件被載入內存後 ,會把不同屬性的內存放在同一個區塊中, 例如輸入表 這是唯讀的屬性 可讀不可寫的屬性 這是可能會把只讀的常量放在同一個段 因為相同的屬性 我們的PE文件頭裡有一個IMAGE_OPTIONAL_HEADER32 裡有有一個DataDirectory的數組, 裡面有很多表的位置(PS這不是區塊表/節表),這數組他是用來查一些 已經定義好的表的地址, 例如輸入表 輸出表 重定位表等等...... 要了解輸入表,我們必需知道什麼是 輸入函數(Import Function), 也稱導入函數, 輸入函數就是被程序 調用但其執行代碼又不在程序中的函數中,這些函數的代碼位於相關的DLL文件中 我們要用W32Dasm 這是一個靜態反匯編工具, 下面可以下載, 有些人不懂什麼是靜態和動態, 意思就是 把程序本來的代碼給反匯編,可不包外部檔如DLL檔, 可是動態是一直去挖所有地方也會去一次由調試器帶領. 這是一個簡單的只調用了MessageBox函數的程序 超簡單 是為了方便學習調試. 代碼: 透過w32dasm程序 反匯編他 得出了以上結果 , 我在看紅線的那個數值 是導入表的數值 可是我們現在沒有學習導入表的運作...

Windows安全之PE結構(三) 之 區塊,區塊的對齊值,RVA和物理內存地址的轉揍

區塊: ------------------------------------------------------------------------------------------------ 一般PE文件 至少有兩個section(區塊/節) .text段 - 預設的區塊代碼,它的內容全是指令代碼,linker會將所有目標文件的.text塊連接成一個大的 .text區塊,如果使用borland c++ 這個段叫.code ------------------------------------------------------------------------------------------------ .data段 - 預設的讀/寫數據區塊,全局變量,靜態變量一般都放在這裡 ------------------------------------------------------------------------------------------------ .rdata段 - 預設只讀數據區塊,至少有兩個情況要用到.rdata , 一是在Microsoft的連接器產生的EXE文件中, 用於存放調試目錄; 二是用於存放說明字符串 ------------------------------------------------------------------------------------------------ .idata段 - 預設包含其他外來DLL的函數及數據信息,一般俗稱輸入表(Import Address Table , IAT) , 將idata區塊合拼到另一個區塊已成慣例,一般會與.rdata段合拼.edata段 - 輸出表啦這個就是, (Export Address Table , EAT) , 當創建一個輸出api或者數據的可執行文件時,linker 會創建一個.EXP文件,這個.EXP文件就包含了一個.edata區塊, 其會被加入到最後的可執行文件中, 與.idata區塊類似, .edata段經常被合拼到到.text / .tdata 區塊中 ----------------------------------------------------------------...

Windows安全之PE結構(二)之 IMAGE_NT_HEADER(NT頭) 以及其成員(IMAGE_FILE_HEADER / IMAGE_OPTIONAL_HEADER32)

Image
上章 Windows安全之PE結構(一) 重溫一下上章節已經講了幾個的PE結構的區域, 如IMAGE_DOS_HEADER,DOS頭所有都是一整字(word)或雙字(dword),只要記住上一章提及兩個重要的成員變量就行了。當然也可以看一下其他的成員變量,知多也沒壞處。 然而IMAGE_NT_HEADER,就講了幾個IMAGE_NT_HEADER裡面的成員的結構體,IMAGE_FILE_HEADER ,IMAGE_OPTIONAL_HEADER32 這兩個 兩個在PE裡頭不可或缺的結構體,沒了IMAGE_OPTIONAL_HEADER32 整個PE結構體都崩壞了,導出表,導入表,重定位表等等的重要信息都不能被保存了,這些表會在以後的章節詳細講解。 今章將會講一下 節表 或稱 區塊表, 不同地區的譯名而已 英文為 IMAGE_SECTION_HEADER, 其實PE結構經過上一章發現 就是一個線性結構的 ------------------------------------------------------------------------------------------------------ 節 / 區塊 (SECTION) : 存在目的 就是為了將同類的數據歸納在一起。 第一部份 除了節 / 區塊 (SECTION) PE加載器不會做任何預處理 節 / 區塊 會做以下處理: - 內存頁的屬性(只讀/可讀寫) - 節的偏移地址 - 節的尺寸 - 不進行映射的節(.reloc) 重定位(Relocation) 的意義 - 對於DLL檔的加載來說 ------------------------------------------------------------------------------------------------------------ 區塊表 / 節表 區塊表的位置定位?? 沒有一個地方存放區塊表中的地址 但是我們可以透過一些方法知道在IMAGE_OPTIONAL_HEADER後面就是區塊表的起始位置了; 獲取 IMAGE_OPTIONAL_HEADER的基地址(這裡是00000100H) 獲取 IMAGE_FILE_HEADER->FileHeader-...

Window安全之PE結構(一)詳解DOS頭

Image
PE文件(*.exe/ *.sys / *.dll) PE文件是在WINDOWS平台下一個十分重要的數據結構,在本地較小此類文檔,特此記錄一下 PE32 是WINDOWS 32BIT下的PE格式 PE32+ 是WINDOWS 64BIT下的PE格式 不過64BIT與32BIT的分別只在於把原本32位的值改為64位而已 為了對齊所有段,實現內存分頁機制 PE加載器 會把 PE文件由硬盤映射到內存(要注意的是在每段之間 相距512字節(byte),4096字節(byte) = 4KB) 內存分頁機制 主要由於磁盤與內存的傳輸單位以頁為主。 PE文件到內存的映射 CPU不會直接從硬盤讀取數據, 而是從物理內存讀取數據, 因此PE文件需要映射到內存供CPU執行 WINDOWS不會一次把整個PE文件映射到內存 CPU需要讀到什麼資源,才從磁盤提交到物理內存 映射後完全相同 預處理 重定位 PE格式大致分為以下段落: 基本名詞: Imagebase // 表示PE文件加載後的原始地址 加載器會首先嘗試加載到0X40000000H地址 // 可以透過GetModuleHandle(LPCTSTR lp)獲取地址,該參數為路徑 VA //虛擬地址(Virtual address) 在PE文件加載後的4GB虛擬內存的任意地址 RVA //相對偏移地址(relative virtual address) 由任一虛擬地址與ImageBase 之差 //RVA = VA - ImageBase 留意: DWORD 是四個字節(4Byte) WORD 是兩個字節(2Byte) //DOS頭, 不是全部定義也需要理會, 需要留意的只有兩個成員變量 typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header 0 WORD e_magic; // Magic number 2 WORD e_cblp; // Bytes on last page of file 4 WORD e_cp; ...