Posts

Showing posts with the label windows安全

Bridges and PCI Internal works

Image
Introduction Have you ever thought how device memory mapping work internally? This article is going to explain peripheral devices internal since I've been asked couple of time to explain how peripheral devices works and are communicated with processors, for example, LPC controller, SCSI controller, LAN controller. Physical Memory partitioning by Host Bridge / NorthBridge As we all known, There's two way to communicate between processor to PCI devices(in PCI speaking master-slave) , I/O port access or Memory mapped access. For memory mapped access, for the same address, for example - 0xA1000, how hardware distinguish that's for devices, and isn't for system memory access? The secret is hidden by host bridge memory partitioning. In Host bridge / North Bridge, there's memory controller, and graphic controller and the root complex logic that connects to SouthBridge / PCH by Direct Media Interface (DMI) , to handle all device communication from processor. As f...

Windows Mini Class and Class Driver internal research notes

Image
Background ------------------------------------- This is a first windows driver research notes in my blogger. And I will try to keep it simple and clear in this article, for who wants to understand the internal behavior of Class And Mini-Class Driver. ------------------------------------- Audience ------------------------------------- For who wants to understand Mini-Class and Class Driver internal. And understanding more about Hid Device stack. ------------------------------------- Introduction ------------------------------------- First, We are going to take a example of HidUsb and HidClass, the former is a Mini-Class Driver of a HidClass. We can simply think the relationships between them, is a child and parent relationship, Hidusb is one of the child of HidClass. HidClass can be thought as a helper module( like Dll do) in kernel mode, for helping all Driver who wants to join a hid family. ------------------------------------- Research ----...

硬件虛擬化 之VMCS結構

/* VMCS結構: * 1. Guest-state area: //客戶機狀態域(如vmware) , 進入VMM時保存 離開VMM時恢復 Cr0, Cr3, Cr4 Dr7 Rsp, Rip 或對應的32位寄存器 所有段選擇子(包括16位選擇子號,段基址,訪問權限,段大小) GDTR,LDTR 以下MSR: IA32_DEBUGCTL IA32_SYSENTER_CS IA32_SYSENTER_ESP & EIP IA32_PERF_GLOBAL_CTRL IA32_PAT IA32_EFER SMBASE寄存器 Activity State(32bit) //CPU活動狀態 0: Active //活動中 1: HLT //正在執行HLT指令 2: ShutDown //由於3次錯誤,導致關機 3: Wait-for-SIPI //等待主虛擬CPU,發送啟動Startup-IPI Interruptibility State(32bit) //可中斷性狀態 bit[0]: Blocking by STI //表示STI屏蔽目前生效中 bit[1]: Blocking by mov SS //表示MOV SS屏蔽目前生效中 bit[2]: Blocking by SMI //表示SMI屏蔽目前生效中 bit[3]: Blocking by NMI //表示NMI屏蔽目前生效中 bit[31:4]: 0 //保留位, 非零會錯誤 Pending debug Exceptions(64/32bit) bit[3:0]: B3-B0 // 每一位表示對應的斷點狀態, DR7沒有設置為會陷入VMM狀態 bit[11:4]: 保留位 // 清零, 非零則VM entry失敗 bit[12]: enabled bp // 表示最小有一個或多個數據斷點或I/O斷點 斷下 並且他已在DR7激活 bit[14...

虛擬化技術(VT) 之 內存虛擬化(待續...)

Image
內存虛擬化 -------------------------------------------- 傳統ia-32 cpu只能支持單次的虛擬地址到物理地址的轉換 但在虛擬機中, 虛擬機的虛擬地址->虛擬機的物理地址(其實也算是虛擬地址)->真正的物理地址 EPT -------------------------------------------- 在原有的CR3頁表地址映射的基礎上, EPT引入了EPT頁表來實現另一次映射 這樣GVA->GPA->HPA就能由硬件實現, 提高效率 為什麼要兩次轉換 如下圖所示... 最重點是因為客戶機的物理地址也是虛擬出來的 1. Guest World GVA -+ | CR3 -+-> PD -> PT -> GPA ------------------------------------- 2. Then when EPT is enabled GPA -+ | EPTP-+-> EPT PML4 -> EPT PDPT -> EPT PDT -> EPT PT -> HPA

虛擬化技術(VT) 之 CPU中斷虛擬化

中斷虛擬化 --------------------------------------------------- 物理CPU外部中斷流程: - I/O設備通過I/O APIC 或PIC 發出中斷請求 - 中斷請求經由PCI總線發送到系統總線, 最後目標CPU的Local APIC 接收中斷 - 最後判斷是否處理中斷 虛擬CPU的模疑流程: - 每個VCPU有對應的虛擬Local APIC用於接收中斷, 及虛擬I/O APIC, 虛擬PIC 用於發送中斷(由VMM維護) - 虛擬設備調用 I/O APIC發送中斷, 挑選出目標的虛擬Local APIC - Local APIC 利用事件注入,將其注入到相應的VCPU 虛擬PIC --------------------------------- IA-32 PIC的ICW1~4 和OCW1~3 都是通過I/O 端口訪問, 即I/O操作, 那vt-x 可以很容易實現虛擬化 具體而言,這些接口是通過I/O端口 0x20 0x21 0xA0 0xA1 來訪問, 因此VMM可以設置VMCS I/O Bitmap相應的位, 使客戶機訪問端口時陷入vmm 陷入VMM後, 按照PIC硬件規範 對這些接口的定義,實現相應的邏輯 例如: OCW1 的功能是用於操作IMR寄存器, 控制指定中斷是否被屏蔽 因此VMM可以分析客戶機的OCW1命令, 判斷出是對哪個中斷進行屏蔽 或者解除屏蔽 VMM繼而在內部邏輯中記錄指定中斷是否被屏蔽, 那下一次虛擬中斷就不會被提交 物理上I/O設備與PIC 是電氣連線 , 但虛擬環境中由於設備和PIC都是虛擬的因而兩者的交互為直接的函數調用 虛擬I/O APIC ------------------------------------------------- PIC是用於單核CPU的, 對於多核CPU 必須通過I/O APIC發送中斷 因此對於多CPU平台, 必須實現I/O APIC 與物理上類似, 虛擬I/O APIC 最後也是提交LAPIC 提交對應中斷 虛擬LAPIC ------------------------------------------------- LAPIC 是CPU上一個內部部份, 負責接受中斷, 此外還...

虛擬化技術(VT) 之 虛擬CPU結構VCPU 詳解

CPU虛擬化的實現: 硬件虛擬化使用VCPU描述符來描述虛擬CPU(客戶機, 類似操作系統的PCB) 是一個struct (1) VCPU標示信息: 用於標示VCPU的一些基本屬性,如ID號, 這VCPU屬於哪一個客戶機 (2) 虛擬寄存器信息:虛擬的寄存器資源, 在使用intel VT-x 的情況下, 這些內容包括在vmcs中 (3) VCPU狀態信息: 類似進程狀態信息, 標示vcpu當前所處的狀態 , 例如睡眠,運行, 供調度器使用 (4) 額外寄存器或部件信息: 主要指未包含在VMCS的一些寄存器或CPU部件,如浮點寄存器, LAPIC (5) 其他信息: 用於vmm進行優化,或存儲其他信息 由此可見VCPU可以劃分成兩部份,一個是以VMCS為主由硬件使用和刷新部份, 主要是虛擬寄存器部份, 另一個是除VMCS外,由VMM使用和更新的部份 當VMM創建客戶機時, 首先要為他創建VCPU, 整個客戶機的運行實際上可以看作是VMM調度不同的VCPU運行 VCPU的創建: ---------------------------------------------------------------------------- 由於VCPU實際上是一個結構體, 那它的創建實際上就是分配相應大小的內存空間 VCPU涉及很多信息, 通常為多級結構 如第一級可以是各平台通用的內容, 中間包含一個指針指向第二級 物理CPU被供電以後, 硬件會自動將CPU初始化為指定狀態, VCPU的初始化也是一個類似的過程 將VCPU描述符的各個部份分置成可用的狀態, 通常初始化包含如下內容: (1) 分配VCPU標示: 首先標示VCPU屬於哪個客戶機 (唯一標示) (2) 初始化虛擬寄存器: 指初始化VMCS相關域,通常指CPU當前的值 (3) 初始化VCPU 狀態信息: 設置VCPU在調度前需要配置的必要標誌位 (4) 初始化額外部件: 將未被VMCS包含的虛擬寄存器初始化為物理CPU的值,並配置虛擬LAPIC等部份 (5) 始化化其他信息: 根據VMM的實現初始化VCPU的私有數據 VMCS的創建與初始化: ------------------------------------------------------...

虛擬化(VT)技術 之 CPU虛擬化探討

VMX運行模式 只有以下兩種: vmx root狀態 -> vmm運行時的狀態 vmx non-root -> 離開vmm後, 普通CPU的狀態 VMEXIT: 引發VMM的時侯 VMENTRY: 離開VMM的時侯 VMCS: VMCS是指一塊4KB內存, 用於VMEXIT 及VMENTRY切換時 更換CPU狀態 一個CPU只可以綁定一個VMCS 使用VMPTRLD 可以將當前運行這一句的CPU 綁定到對應的VMCS 使用VMCLEAR , 解除綁定現在CPU的VMCS VMCS的變換過程: (1) CPU1 上執行VMCLEAR解除綁定 (2) CPU2 上執行VMPTRLD 綁定新的VMCS VMCS主要偏移: (1) 偏移0 : 是VMCS版本標誌 表示VMCS的數據格式的版本 (2) 偏移4 : VMX中止指示, VMEXIT不成功的時侯產生VMX中止, CPU會在此處存入VMX意外中止的原因 (3) 偏移8 : VMCS數據域,該域的格式是隨著CPU不同而有所變化->具體用偏移0判斷哪一種格式 x86 CPU 提供兩條新引入的匯編指令: VMREAD : 讀取VMCS中 指定的域 VMWRITE : 寫入 中指定的域 VT-x 為VMCS數據域每一個字段也定義了相應的 因此令以上兩條指令成立 具體VMCS數據域包含以下六大信息 (1)客戶機狀態域: 進入VMM時保留, 離開VMM時取回 (2)宿主機狀態域: 進入VMM時取回, 離開VMM時保留 (3)VM-Entry控制域: 控制VM-Entry的過程 (4)VM-Execution控制域: 控制處理器在vmx non-root模式時的行為 (5)VM-Exit控制域: 控制vm-exit的過程 (6)VM-Exit數據域: 提供vm-exit的原因及相關信息, 注意這是只讀(read-only)的 (1) 客戶機狀態域: 用於保存CPU在NON-ROOT模式下的CPU上下文(Context) VM-Entry時會提取恢復到CPU, VM-Exit時會保留 它保存了段寄存器, CR3, IDTR, GDTR等寄存器 CPU通過他們來切換實現客戶機地址空間與VMM地址空間的切換 客戶機狀態域並不包...

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安全 之 內存映射文件(Section對像)MmCreateSection與MmMapViewOfSection 流程分析

Image
MmCreateSection 內存區(Section)內核對像有兩種,但都是基於分頁內存的: 一種是基於頁面文件的 一種是基於其他文件,以文件空間作為基礎的 如EXE在硬盤上的空間 MmCreateSection主要目的: 1. 填充CONTROL_AREA對像 2. 填充SEGMENT對像 3. 調用ObCreateObject創建Section內核對像 4. 返回Section內核對像 SEGMENT是真正描述映射區域的對像 , 內核函數MmCreateSection中有三種創建方式: 1. MiCreatePagingFileMap //以PageFile(頁面文件)為基礎的文件共享 2. MiCreateImageFileMap //以可執行映像作文件共享 3. MiCreateDataFileMap //以普通數據文件作為基 p.s. 後兩者雖表面內核文件對像, 從函數內部邏輯可發現, 如果映射同一個文件時, Section對像會共用同一個Segment對像 由於Section對像的創建完全是建基於SEGMENT對像, 因此探討SEGMENT對像才是正確方向 了解共享實現 SEGMENT(段對像,分配在分頁內存): 1. 分配的共享空間頁面總數量大小的MMPTE陣列緊隨著SEGMENT對像(用於建立映射視圖時) 2. 指向CONTROL_AREA指針(CONTROL_AREA, 分配在非分頁內存,即物理內存, 它亦會指向SEGMENT對像, 建立互指的關係) 3. 記錄對應節中的所有頁面的數量 4. 全部MMPTE最終指向節的起始地址 建數內部會再次創建真正的: CONTROL_AREA(控制區對象,分配在分頁內存) 1. 指向文件對像指針 2. 尾隨著多個SUBSECTION對像 (指PE中各個節的起始地址,使用Segment的MMPTE進行初始化->再讓VAD中會使用SubSection) 3. SUBSECTION形成鏈表,其中一個指針會指回CONTROL_DATA SubSection結構如下: typedef struct _SUBSECTION { PCONTROL_AREA ControlArea; PMMPTE SubsectionB...

軟件調試(12) - Win32堆,CRT堆簡介及堆溢出攻擊及系統檢測溢出方案

Win32堆與CRT堆 ------------------------------------------------ 堆分配函數: malloc / HeapAlloc/ new 堆的基本運作: 內存管理器(Memory Manager)把一塊較大的內存空間,委托給堆管理器(Heap Manager)來管理,堆管理器將大塊內存分割成不同大小的很多個小塊來滿足應用程序的需要 而實現內存委托的一系列函數,稱為池管理器(Pool Manager) Windows在創建進程時,加載器函數執行進程用戶態初始化階段會調用RtlCreateHeap函數為新的進程創建第一個堆 稱為默認堆 默認堆: 由LdrpInitializeProcess所調用RtlCreateHeap建立 此時新進程中只有EXE模塊和NTDLL模塊,EXE模塊中的用戶代碼還未執行,創建好的堆句柄會存放在PEB中ProcessHeap字段, 與棧類似也有HeapSegmentReserve和HeapSegmentCommit兩個字段來決定默認堆的保留大小和提交大小 分配私有Win32堆: 用戶透過HeapCreate函數創建自己的堆 函數原型: HANDLE WINAPI HeapCreate( _In_ DWORD flOptions, _In_ SIZE_T dwInitialSize, _In_ SIZE_T dwMaximumSize ); 刪除則是調用: BOOL WINAPI HeapDestroy( _In_ HANDLE hHeap ); 堆列表: 每個進程的PEB結構以列表方式記錄當前進程的所有堆句柄: NumberOfHeap 是堆總數 ProcessHeaps 每個堆的句柄,它是一個數組 MaximumNumberOfHeaps 可分配堆總數 如果NumberOfHeap == MaximumNumberOfHeaps 堆管理器則增大MaximumNumberOfHeaps 的值並重新分配ProcessHeaps 分配堆空間: 使用HeapAlloc可以在指定堆中分配空間 函數原形: LPVOID WINAPI HeapAlloc( _In_ HANDLE hHeap, _In_ DWORD dwFlags,...

軟件調試(11) - 棧安全檢查,溢出及緩沖區溢出

用戶態及內核態棧 -------------------------------------------- TSS會記錄了不同優先級所使用的棧的基本信息 TSS+0x4 到 TSS+0x28的24字節是用來記錄棧的段信息和棧指針ss:esp 每個Win32線程都有兩個棧: 一是用戶態棧,記錄在_TEB->_NT_TIB 一是內核態棧,記錄在_KTHREAD 他們記著線程棧的詳細資料 用戶態棧- 1MB x86內核態棧-12KB x64內核態棧-24KB 考慮到gui線程在調用gdi服務時通常需要更大的棧段空間,所以在一個線程被轉為GUI線程後,內核態棧一般會被替換成一個更新的新棧 -------------------------------------------- 內核棧的創建: PspCreateThread是Windows內核態用於創建線程的一個重要內部函數 不論是PspCreateSystemThread或NtCreateThread 由PspCreateThread創建內核態棧,大小總是默認大小的內核態棧 但Windows線程在第一次調用Win32子系統服務時,將其轉變為GUI線程->轉化後系統的PsConvertToGuiThread函數會為該線程重新建立一個棧,然後使用KeSwitchKernelStack切換到新的棧,新的棧是可以改變大小的,稱為大內核態棧(Large Kernel Stack) 棧大小的最大值記錄在MmLargeStackSize 在一個線程被轉變為GUI線程 其KTHREAD結構的LargeStack會改為1 同時其Win32Thread字段會由0變為非0 棧段不會立即增大,在需要增大時調用MmGrowKernelStack函數來增長棧 ------------------------------------------------------- 用戶態棧的創建 初始線程: CreateProcess->NtCreateProcess->BaseCreateStack(調用NtCreateThread前)初始化用戶態棧 初始線程外: CreateThread / CreateRemoteThread->BaseCreateSt...

軟件調試(10) - 棧和函數調用

用戶態及內核態棧 -------------------------------------------- TSS會記錄了不同優先級所使用的棧的基本信息 TSS+0x4 到 TSS+0x28的24字節是用來記錄棧的段信息和棧指針ss:esp 每個Win32線程都有兩個棧: 一是用戶態棧,記錄在_TEB->_NT_TIB 一是內核態棧,記錄在_KTHREAD 他們記著線程棧的詳細資料 用戶態棧- 1MB x86內核態棧-12KB x64內核態棧-24KB 考慮到gui線程在調用gdi服務時通常需要更大的棧段空間,所以在一個線程被轉為GUI線程後,內核態棧一般會被替換成一個更新的新棧 -------------------------------------------- 內核棧的創建: PspCreateThread是Windows內核態用於創建線程的一個重要內部函數 不論是PspCreateSystemThread或NtCreateThread 由PspCreateThread創建內核態棧,大小總是默認大小的內核態棧 但Windows線程在第一次調用Win32子系統服務時,將其轉變為GUI線程->轉化後系統的PsConvertToGuiThread函數會為該線程重新建立一個棧,然後使用KeSwitchKernelStack切換到新的棧,新的棧是可以改變大小的,稱為大內核態棧(Large Kernel Stack) 棧大小的最大值記錄在MmLargeStackSize 在一個線程被轉變為GUI線程 其KTHREAD結構的LargeStack會改為1 同時其Win32Thread字段會由0變為非0 棧段不會立即增大,在需要增大時調用MmGrowKernelStack函數來增長棧 ------------------------------------------------------- 用戶態棧的創建 初始線程: CreateProcess->NtCreateProcess->BaseCreateStack(調用NtCreateThread前)初始化用戶態棧 初始線程外: CreateThread / CreateRemoteThread->BaseCreateSt...