Posts

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

Windows安全之 應用層(Ring3)的)內存管理(四) 堆(Heap)

Image
4. Windows 最小單位的內存管理 - 堆 進程的默認堆是1MB, 透過修改鏈接器的/HEAP 屬性可以自定義大小 相比之前兩章, 堆是用來管理鏈表和樹的最好內存結構, 堆的優點就是可以不顧分配粒度, 和頁面邊界的事情, 但缺點就是分配及釋放比較慢一點, 而且也沒法再對物理存備器調撥進行直接控制。 4.1 創建額外的堆  HANDLE WINAPI HeapCreate( _In_ DWORD  flOptions, _In_ SIZE_T dwInitialSize, _In_ SIZE_T dwMaximumSize  ); 使用創建 HeapCreate 額外的堆 使用額外的堆 可使線程開銷減低, 而且把儲存對象分類, 不然如果把鏈表及二叉樹的對象放在同一個堆中, 會發現鏈表的節點有可能會令二叉樹的節點被破壞,  如下圖 創建額外的堆的原因 假設NODE1 有代碼會覆蓋它的下8個字節的地方, 這樣就會使NODE1及BRAJNCH2,3 被破壞 所以我們是需要創建額外的堆來保護我們的組件, 不應把所有類別都在放同一個堆中 使用堆, 能夠快速釋放, 例如我們將一個二叉樹的所有節點 進行釋放, 需要遍歷所有節點進行釋放, 但現在 我們使用堆存放著所有節點, 我們只需要把這個堆釋放, 例如Windows的資源管理器使用二叉樹來遍歷系統所有進程, 那如果刷新一次, 他就需要重新遍歷一次系統 看進程是否已經存在, 但使用堆就可以直接清除, 最後重新遍歷, 不用再比對什麼 4.2 從堆中分配內存塊 使用以下函數 LPVOID WINAPI HeapAlloc( _In_ HANDLE hHeap, //指定哪一塊堆中建立內存塊 _In_ DWORD  dwFlags, _In_ SIZE_T dwBytes ); 使用創建HeapAlloc在堆中分配一塊內存,  如果成功返回內存塊地址, 若較大的內存分配應直接使用VirtualAlloc , 而不使用堆函數 LPVOID WINAPI HeapReAlloc( _In_ HANDLE hHeap, ...