Posts

Showing posts from February, 2016

軟件調試(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

軟件調試(9) - 深入探討Windows結構化異常(SEH)底層實現

r3/r0一樣, FS:[0] 保存著SEH的鏈表頭 應用層: TEB與TIB結構 TEB中的起始處是指向TIB的地址,TIB第一個字段位exceptionList即為鏈表表頭 內核層 KPCR與TIB結構 KPCR->TIB TIB的ExceptionList字段 ------------------------------------------------------- 該字段位於TIB的起始處 typedef struct _EXCEPTION_REGISTERATION_RECORD{ struct _EXCEPTION_REGISTERATION_RECORD *next; //0xFFFFFFFF代表最後一個節點 ULONG handler; //異常處理器的地址(需符合seh函數原形) } EXCEPTION_DISPOSITION SehHandler(_EXCEPTION_RECORD *exceptionRecord, //處理的異常 void * EstablisherFrame, //異常函數的EBP _CONTEXT *contextRecord, //線程上下文 void * DispatcherContext //分發函數 ) TIB自身不在棧中,而SEH鏈表則是存在棧中 登記異常函數 ------------------------------------------------------- push seh_handler //處理函數地址 棧空間: push FS:[0] //舊的鏈表節點處理器地址 FS:[0] move FS:[0],ESP //登記新結構 SEH_HANDLER ..... mov esp,dword ptr fs:[0] //保存下一個結構地址 pop dword ptr fs:[0] //將下一個結構的地址變成鏈表頭 用戶態版本RtlDispatchException ------------------------------------------------------- (內核中的異常分發:修改EIP到R3的KiUserExceptionDispatcher->)RtlD

軟件調試(8) - 探討從應用層到內核層的中斷與異常管理

硬件異常 IDT 在idtr讀取idt表基址 ------------------------- 門描述符: 任務門 - 用於任務切換 里面包含用於選擇任務狀態段(tss)的段選擇子 中斷門 - 用於描述中斷處理例程入口 陷阱門 - 用於描述異常處理例程入口 任務門: 描述的是一個TSS段->CPU要做的是切換到這個TSS段所代表的線程,然後開始執行這個線程,TSS段是用來保存任務信息的一段內存區,其格式由CPU定義 Windows會為每個CPU創建3-4個tss 1: 處理NMI 2: 處理#DF(double fault) 異常中再異常 3: 處理機器檢查異常 軟件異常 -------------------------- EXCEPTION_RECORD 結構 typdef struct _EXCEPTION_RECORD{ DWORD ExceptionCode //異常代碼 DWORD ExceptionFlags //異常標志 struct _EXCEPTION_RECORD* ExceptionRecord; //相關的另一個異常 PVOID ExceptionAddress; //異常發生地址 DWORD NumberParameters; //參數數組元素 ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; //參數數組 }EXCEPTION_RECORD, *PEXCEPTION_RECORD; #define EXCEPTION_BREAKPOINT STATUS_BREAKPOINT #define EXCEPTION_SINGLE_STEP STATUS_SINGLE_STEP 登記CPU異常 --------------------------- KiTrapXX例程在完成針對本異常的動作後,會調用CommonDispatchException->KiDispatchException 並通過寄存器將如下信息傳送到函數 1. 異常代碼->EAX 2. 異常指令地址->EBX 3. 其他信息作為附帶參數(最多3個),EDX(參數1),ESI(參數2),

軟件調試(7) - 調試體系之內核調試原理

內核調試器通過內核調試引擎 控制或調試內核 內核其他部份->內核調試引擎->內核調試器 調試器通過內核調試協議 訪問和控制目標系統 內核調試API: KdAPI - 類似遠程調用方式訪問內核 與系統內核的接口函數 與調試器通信函數 斷點管理 - 使用KdpBreakpointTable記錄斷點 內核調試api - 提供調試信息給內核調試引擎 系統內核控制函數 - KdEnterDebugger 與 KdExitDebugger 管理函數 - KdEnableDebugger 與KdDisableDebugger ETW支持函數 - event log... 驅動程序更新服務 - 本地內核調試支持(XP後) kdcom.dll 負責通信部份 收/發數據包 - KdSendPacket - KdReceivePacket 接收電源狀態變化 - KdD0Transition ,喚醒系統用 - KdD3Transition ,休眠時用 內核調試引擎初始化: BIOS->Harddisk引導程式->執行NTLDR.exe->CPU初始化->切換保護模式->啟用分頁機制->通過配置boot.ini得到windows系統的系統目錄並加載系統內核文(ntoskrnl.exe)先檢查他的導入表並加載依賴文(包括內核調試通信模塊KDCOM.dll)->讀取注册表的hive->加載其中定義的啟動類型的驅動程序->完成所有工作->找到內核文件的PE文件頭找到入口函數->kISystemStartup->進入初始化三部曲-> 1. 調用HalInitializeProcessor() 初始化CPU 2. 調用KdInitSystem 初始化內核調試引擎 3. 調用KiInitializeKenrel開始內核初始化 KiInitializeKenrel->KeInitializeThread -> ExpInitializeExecutive() ExpInitializeExecutive進行大量工作包括進程管理器初始化: 進程管理器初始化: - 定義進程/線程內核對象類型 - 建立記錄系統中所有進程的鏈

軟件調試(6) - 深入探討調試體系啟動及附加調試

用戶 <=== UI線程 <=== 調試器 ===> 調試工作線程 ===> 被調試進程 (獲取調試對象) (設置調試對象) 調試器 ----------------------------------------------------------------- DbgSsReserved[0] => 被調試線程鏈表頭 DbgSsReserved[1] => 調試對象的句柄 //判斷是否句柄 !handle xxxxx 鏈表頭結構: typedef struct _DBGSS_THREAD_DATA { struct _DBGSS_THREAD_DATA *Next; HANDLE ThreadHandle; HANDLE ProcessHandle; DWORD ProcessID; DWORD ThreadID; BOOLEAN HandleMarked; //退出標志 }DBGSS_THREAD_DATA,*PDBGSS_THREAD_DATA; WaitForDebugEvent , ContinueDebugEvent 會維護DbgSsReserved[0]鏈表 WinXP=> Ntdll!DbgUiGetThreadDebugObject 和Ntdll!DbgUiSetThreadDebugObject 實際上就是讀寫鏈表 WaitForDebugEvent => 從teb獲取調試對象 DbgSsReserved[1] 由kernel32的調試API所設置, 如果Windbg不是調用他來調試 *而CreateProcess時R3層會把他設置為非0 但在CreateProcess後Windbg會調用DbgUiSetThreadDebugObject設置為0 被調試進程 和原本進程的差異 ------------------------------------------------------------------ 1. EPROCESS Debugport字段不為0, 內核空間判斷一個進程是否正在被調試的主要特征 2. PEB的BeingDebugged字段不為0, 用戶態判斷是否正在被調試的主要方法 3. 可

軟件調試(5) - 深入探討Windows調試體系(調試信息傳遞)

參與者: 被調試進程 調試進程 調試子系統(內核) 調試子系統主要分為3部份: - 位於NTDLL中的支持函數 DbgUi - 位於內核文件中的支持函數 DbgSs - 調試子系統服務器 Dbg while(WaitForDebugEvent(&DbgEvt,INFINITE)) { //處理等待得到的事件 //處理後恢復調試目標繼續執行 ContinueDebugEvent(DbgEvt.dwProcessID, DbgEvt.dwThreadId,dwContinueStatus); } WaitForDebugEvent - 用於等待和接收調試事件,收到事件後,調試器會根據事件的類型(事件ID)來分發和處理, 在處理調試事件的過程中,被調試進程是處於掛起狀態,處理調試事件後,調試器用ContinueDebugEvent將處理結果回復給調試子系統,讓被調試程序繼續執行,調試器則再次調用WaitForDebugEvent 等下一個調試事件 調試子系統的內核部份 ------------------------------------ 收集調試事件後=>以一個消息結構發送給調試子系統 => 使其保存在調試子系統的調試消息隊列中 => 子系統與調試器靠一個內核對象來同步=>當有調試消息需要讀取=>調試子系統服務器會設置這個待器線程被喚起 內核中: DBGKM_APIMSG 調試API: DEBUG_EVENT 由於兩者結構不一, 需要轉化過程 子系統服務器會將自己使用的結構轉化為NTDLL使用的DEBUG_WAIT_STATE_CHANGE, NTDLL再將這個結構轉化為調試器使用的DEBUG_EVENT結構 內核中Dbgk收集例程將所有調試事件分為8類 -------------------------------------------------- typedef enum _DBGKM_APINUMBER { DbgkmExceptionApi = 0, // 异常 DbgkmCreateThreadApi = 1, // 创建线程 DbgkmCreateProcessApi = 2, // 创建进程 DbgkmExitThreadApi = 3

軟件調試(4) - 探討內核進程對象與內核簡介

每個Win32進程擁有以下元素: - 一個全局唯一的PID - 一個可執行映象(IMAGE) - 一個或多個線程 - 一個EPROCESS位於內核空間 - 一個對象句柄表 用以記錄或索引該進程所創建/打開的內核對象 - 一個用於描述內存目錄表起始位址的基址,簡稱頁目錄基址(DirBase),當cpu切換時會將該地址加載到cr3 -> 一直索引到真實存在的物理地 - 一個位於用戶空間中的進程環境塊(peb) , 被內核空間映射到用戶空間 - 一個訪問權限令牌(access token) 用於表示進程用戶,安全組,優先級 !process 0 0 第一個為pid, 0代表所有進程,第二個0指定要顯示的進程屬性,0代表進程基本屬性 EPROCESS ------------------------------------ dt _EPROCESS 86a7d030 把目標地址用_EPROCESS結構解析 EPROCESS有兩個與調試有關的位 - DebugPort ; EPROCESS+0xBC - ExceptionPort ; EPROCESS+0xC0 PEB ------------------------------------- PEB是進程環境塊, 它包含了進程的大多數用戶態信息, 與EPROCESS結構是位於內核空間不同 PEB是在內核態建立後,映射到用戶空間, 因此,在一個系統中,多個進程的PEB地址可能是同一個值 因為PEB是生存在R3 .process 86a7d030 <<< 先指明進程eprocess位置 dt _PEB 7ffdf000 <<< 才能使用dt _PEB 查看進程peb SESSION ID -------------------------------------- 進程的SESSION ID 是指該進程所在的WINDOWS會話的ID號, 當有多個用戶同時登錄時,WINDOWS會為每個用戶建立一個會話, - 每個會話有自己的WorkStation和卓面 - 這樣可以工作在不同的會話中共用同一個windows系統 - 對於典型xp系統,當只有一個用戶登錄時,用戶啟動的程序和系統服務都在session 0 - 當切換到

軟件調試(3) - 調試異常與斷點異常

保護模式下的INT 3 INT 3 對應的異常處理函數是內核函數: nt!KiTrap03 斷點命中 --------------------- 由於是R3程序調試 =>會因斷點指令由R3轉入R0 =>由內核函數分發(11章詳解) =>由於異常來自r3, 而產生異常的進程正被調試(debugport非0),所以內核例程會把這個異常通過調試子系統,以調試事件的形式分發給r3的調試器,例如(vc6),在通知調試器後, 內核等待回復=>收到回復後調試子系統的函數會層層返回,最後返回到異常處理例程 KiTrap03會把程序指針寄存器-1 => 因而調試器看到的仍然是int3的位置 看似未被執行=>但其實已經執行 1. 保持原有指令完整性 2. 由於斷點存在, 原本指令其實未被執行=>因應該指向原有指令位置(改為INT 3 的地址) 恢復執行 ----------------------- 當用戶結束分析希望恢復時 =>調試器通過調試api通知調試子系統(內核), 這會導致系統內核的異常分發函數返回到異常處理例程,然後異常處理例程通過IRET/IRETD指令觸發一個異常返回動作 =>使CPU恢復上下文,從發生異常的位置繼續執行 =>注意:EIP指向斷點的位置,最後恢復指令 =>CPU繼續執行 特別用途 ------------------------ 0xCC塊的作用: 為調試版本=>分配緩沖區=>因為緩沖區或堆棧溢出時,程序指針意外指向了這些區域便因為INT3 而馬上中斷到調試器 還有就是填充函數或代碼段未尾的空閑空間,也就是用來做內存對齊=>截獲內存溢出 斷點API ------------------------ R0=>DbgBreakPoint / DbgBreakPointWithStatus R3=>DebugBreak 調試寄存器 ------------------------ DR0~DR3 : 指定斷點地址, 是虛擬地址不是物理地址, 因為CPU是在虛擬地址被翻譯之前做斷點工作, 意味不能透過它對物理內存作斷點行為 DR7 : 24位被分成四組 分別與四個調試寄存器對應,用於設置

軟件調試(2) - 異常中斷與中斷向量表(IDT)簡介

IA-32 中斷異常列表: No. 助記符 類型 描述 來源 0 #DE 錯誤 除零錯誤 div / idiv指令 1 #DB 錯誤/陷阱 調試異常 任何代碼或數據引用 2 中斷 NMI中斷 不可屏蔽的外部中斷 3 #BP 陷阱 斷點 INT3指令 4 #OF 陷阱 溢出 INT0指令 5 #BR 錯誤 數組出界 BOUND指令 6 #UD 錯誤 無效指令(沒定義) UD2指令 7 #NM 錯誤 數學協處理器不存在 浮點或WAIT/FWAIT指令 8 #DF 中止 雙重錯誤 任何可能產生異常的指令/任意中斷 9 #MF 錯誤 向協處理器傳送操作數, 浮點指令 時檢測到頁錯誤,或段不存 在. 10 #TS 錯誤 無效TSS(任務狀態段) 任務切換或訪問TSS 11 #NP 錯誤 段不存在 加載段寄存器或訪問系統段 12 #SS 錯誤 棧段錯誤 棧操作或加載SS寄存器 13 #GP 錯誤 通用保護(GP)異常,如果 任何內存引用和保護性檢測 一個操作違反了保護模式 下的約定,而且該情況不屬 於其他異常,則產生GP 14 #PF 錯誤 頁錯誤 任何內存引用 15 保留 16 #MF 錯誤 浮點錯誤 浮點或WAIT/FWAIT指令 17 #AC 錯誤 對齊檢查 對內存中數據的引用 18 #MC 中止 機器檢查 錯誤代碼和來源與型號有關 19 #XF 錯誤 SIMD浮點異常 SIMD浮點指令 20 保留 32-255 用戶自定義中斷 可屏蔽中斷 錯誤代碼 31 3 2 1 0 ----保留--------------- -TI- -IDT- -EXT- 位0: EXT 如果為1表示外部事件導致異常 位1: IDT 描述符位置,如果為1表示錯誤碼的段選擇子索引指向的是IDT表中的門描述符,如果0表示索引部份指向的是LDT/GDT的描述符 位2: TI(GDT/LDT, 當IDT位為0時有效), 0:GDT / 1:LDT IA-32中斷異常的優先級 1(最高級) 硬件重啟動和機器檢查異常 2 任務切換陷阱 3 外部硬件(例如芯片組)通過CPU引腳發給CPU的特別干預 #F

軟件調試(1) - 斷點簡介

IA結構CPU 提供的調試支持: INT3 指令 - 斷點指令, 當CPU執行到該指令時便會產生斷點異常以便中斷9到調試器 -> 軟中斷 EFLAGS中的TF標志位: 陷阱標志位,當標志位為1: CPU每執行完一條指令就產生調試異常 -> 單步執行 調試寄存器: DR0~DR7 -> 用於設置硬件斷點和報告調試異常的細節 斷點異常(#BP): 當INT3 指令執行時,產生此異常, CPU將會轉到該異常的處理函數, 他會進一步分發到調試器 調試異常(#DB): 當除INT3 指令以外的調試發生時, 產生此異常 TSS的T標志位:任務陷阱標志,當切換到設置了T標志的任務時,CPU會產生調試異常,中斷到調試器 參考:Windows軟件調試