国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

黑客程序設計

2019-11-17 04:37:55
字體:
來源:轉載
供稿:網友
 Windows內核調試器原理淺析
前段時間忽然對內核調試器實現原來發生了愛好,于是簡單分析了一下當前windows下主流內核調試器原理,并模擬原理自己也寫了個極其簡單的調試器:)
WinDBG
WinDBG和用戶調試器一點很大不同是內核調試器在一臺機器上啟動,通過串口調試另一個相聯系的以Debug方式啟動的系統,這個系統可以是虛擬機上的系統,也可以是另一臺機器上的系統(這只是微軟推薦和實現的方法,其實象SoftICE這類內核調試器可以實現單機調試)。很多人認為主要功能都是在WinDBG里實現,事實上并不是那么一回事,windows已經把內核調試的機制集成進了內核,WinDBG、kd之類的內核調試器要做的僅僅是通過串行發送特定格式數據包來進行聯系,比如中斷系統、下斷點、顯示內存數據等等。然后把收到的數據包經過WinDBG處理顯示出來。
在進一步介紹WinDBG之前,先介紹兩個函數:KdpTrace、KdpStub,我在《windows異常處理流程》一文里簡單提過這兩個函數。現在再提一下,當異常發生于內核態下,會調用KiDebugRoutine兩次,異常發生于用戶態下,會調用KiDebugRoutine一次,而且第一次調用都是剛開始處理異常的時候。
當WinDBG未被加載時KiDebugRoutine為KdpStub,處理也很簡單,主要是對由int 0x2d引起的異常如DbgPRint、DbgPrompt、加載卸載SYMBOLS(關于int 0x2d引起的異常將在后面具體介紹)等,把Context.Eip加1,跳過int 0x2d后面跟著的int 0x3指令。
真正實現了WinDBG功能的函數是KdpTrap,它負責處理所有STATUS_BREAKPOINT和STATUS_SINGLE_STEP(單步)異常。STATUS_BREAKPOINT的異常包括int 0x3、DbgPrint、DbgPrompt、加載卸載SYMBOLS。DbgPrint的處理最簡單,KdpTrap直接向調試器發含有字符串的包。DbgPrompt因為是要輸出并接收字符串,所以先將含有字符串的包發送出去,再陷入循環等待接收來自調試器的含有回復字符串的包。SYMBOLS的加載和卸載通過調用KdpReportSymbolsStateChange,int 0x3斷點異常和int 0x1單步異常(這兩個異常基本上是內核調試器處理得最多的異常)通過調用KdpReportExceptionStateChange,這兩個函數很相似,都是通過調用KdpSendWaitContinue函數。KdpSendWaitContinue可以說是內核調試器功能的大管家,負責各個功能的分派。這個函數向內核調試器發送要發送的信息,比如當前所有寄存器狀態,每次單步后我們都可以發現寄存器的信息被更新,就是內核調試器接受它發出的包含最新機器狀態的包;還有SYMBOLS的狀態,這樣加載和卸載了SYMBOLS我們都能在內核調試器里看到相應的反應。然后KdpSendWaitContinue等待從內核調試器發來的包含命令的包,決定下一步該干什么。讓我們來看看KdpSendWaitContinue都能干些什么:

case DbgKdReadVirtualMemoryApi:
KdpReadVirtualMemory(&ManipulateState,&MessageData,ContextRecord);
break;
case DbgKdReadVirtualMemory64Api:
KdpReadVirtualMemory64(&ManipulateState,&MessageData,ContextRecord);
break;
case DbgKdWriteVirtualMemoryApi:
KdpWriteVirtualMemory(&ManipulateState,&MessageData,ContextRecord);
break;
case DbgKdWriteVirtualMemory64Api:
KdpWriteVirtualMemory64(&ManipulateState,&MessageData,ContextRecord);
break;
case DbgKdReadPhysicalMemoryApi:
KdpReadPhysicalMemory(&ManipulateState,&MessageData,ContextRecord);
break;
case DbgKdWritePhysicalMemoryApi:
KdpWritePhysicalMemory(&ManipulateState,&MessageData,ContextRecord);
break;
case DbgKdGetContextApi:
KdpGetContext(&ManipulateState,&MessageData,ContextRecord);
break;
case DbgKdSetContextApi:
KdpSetContext(&ManipulateState,&MessageData,ContextRecord);
break;
case DbgKdWriteBreakPointApi:
KdpWriteBreakpoint(&ManipulateState,&MessageData,ContextRecord);
break;
case DbgKdRestoreBreakPointApi:
KdpRestoreBreakpoin(&ManipulateState,&MessageData,ContextRecord);
break;
case DbgKdReadControlSpaceApi:
KdpReadControlSpace(&ManipulateState,&MessageData,ContextRecord);
break;
case DbgKdWriteControlSpaceApi:
KdpWriteControlSpace(&ManipulateState,&MessageData,ContextRecord);

break;
case DbgKdReadIoSpaceApi:
KdpReadIoSpace(&ManipulateState,&MessageData,ContextRecord);
break;
case DbgKdWriteIoSpaceApi:
KdpWriteIoSpace(&ManipulateState,&MessageData,ContextRecord);
break;
case DbgKdContinueApi:
if (NT_SUCCESS(ManipulateState.u.Continue.ContinueStatus) != FALSE) {
return ContinueSuccess;
} else {
return ContinueError;
}
break;
case DbgKdContinueApi2:
if (NT_SUCCESS(ManipulateState.u.Continue2.ContinueStatus) != FALSE) {
KdpGetStateChange(&ManipulateState,ContextRecord);
return ContinueSuccess;
} else {
return ContinueError;
}
break;
case DbgKdRebootApi:
KdpReboot();
break;
case DbgKdReadMachineSpecificRegister:
KdpReadMachineSpecificRegister(&ManipulateState,&MessageData,ContextRecord);
break;

case DbgKdWriteMachineSpecificRegister:
KdpWriteMachineSpecificRegister(&ManipulateState,&MessageData,ContextRecord);
break;
case DbgKdSetSpecialCallApi:
KdSetSpecialCall(&ManipulateState,ContextRecord);
break;
case DbgKdClearSpecialCallsApi:
KdClearSpecialCalls();
break;
case DbgKdSetInternalBreakPointApi:
KdSetInternalBreakpoint(&ManipulateState);
break;
case DbgKdGetInternalBreakPointApi:
KdGetInternalBreakpoint(&ManipulateState);
break;
case DbgKdGetVersionApi:
KdpGetVersion(&ManipulateState);
break;
case DbgKdCauseBugCheckApi:
KdpCauseBugCheck(&ManipulateState);
break;
case DbgKdPageInApi:
KdpNotSupported(&ManipulateState);
break;
case DbgKdWriteBreakPointExApi:
Status = KdpWriteBreakPointEx(&ManipulateState,
&MessageData,
ContextRecord);
if (Status) {
ManipulateState.ApiNumber = DbgKdContinueApi;
ManipulateState.u.Continue.ContinueStatus = Status;
return ContinueError;
}
break;
case DbgKdRestoreBreakPointExApi:
KdpRestoreBreakPointEx(&ManipulateState,&MessageData,ContextRecord);
break;
case DbgKdSwitchProcessor:
KdPortRestore ();
ContinueStatus = KeSwitchFrozenProcessor(ManipulateState.Processor);
KdPortSave ();
return ContinueStatus;
case DbgKdSearchMemoryApi:
KdpSearchMemory(&ManipulateState, &MessageData, ContextRecord);
break;
讀寫內存、搜索內存、設置/恢復斷點、繼續執行、重啟等等,WinDBG里的功能是不是都能實現了?呵呵。
每次內核調試器接管系統是通過調用在KiDispatchException里調用KiDebugRoutine(KdpTrace),但我們知道要讓系統執行到KiDispatchException必須是系統發生了異常。而內核調試器與被調試系統之間只是通過串口聯系,串口只會發生中斷,并不會讓系統引發異常。那么是怎么讓系統產生一個異常呢?答案就在KeUpdateSystemTime里,每當發生時鐘中斷后在HalpClockInterrupt做了一些底層處理后就會跳轉到這個函數來更新系統時間(因為是跳轉而不是調用,所以在WinDBG斷下來后回溯堆棧是不會發現HalpClockInterrupt的地址的),是系統中調用最頻繁的幾個函數之一。在KeUpdateSystemTime里會判定KdDebuggerEnable是否為TRUE,若為TRUE則調用KdPollBreakIn判定是否有來自內核調試器的包含中斷信息的包,若有則調用DbgBreakPointWithStatus,執行一個int 0x3指令,在異常處理流程進入了KdpTrace后將根據處理不同向內核調試器發包并無限循環等待內核調試的回應。現在能理解為什么在WinDBG里中斷系統后堆棧回溯可以依次發現KeUpdateSystemTime->RtlpBreakWithStatusInstruction,系統停在了int 0x3指令上(其實int 0x3已經執行過了,只不過Eip被減了1而已),實際已經進入KiDispatchException->KdpTrap,將控制權交給了內核調試器。


系統與調試器交互的方法除了int 0x3外,還有DbgPrint、DbgPrompt、加載和卸載symbols,它們共同通過調用DebugService獲得服務。
NTSTATUS DebugService(
ULONG ServiceClass,
PVOID Arg1,
PVOID Arg2
)
{
NTSTATUS Status;
__asm {
mov eax, ServiceClass
mov ecx, Arg1
mov edx, Arg2
int 0x2d
int 0x3
mov Status, eax
}
return Status;
}
ServiceClass可以是BEAKPOINT_PRINT(0x1)、BREAKPOINT_PROMPT(0x2)、BREAKPOINT_LOAD_SYMBOLS(0x3)、BREAKPOINT_UNLOAD_SYMBOLS(0x4)。為什么后面要跟個int 0x3,M$的說法是為了和int 0x3共享代碼(我沒弄明白啥意思-_-),因為int 0x2d的陷阱處理程序是做些處理后跳到int 0x3的陷阱處理程序中繼續處理。但事實上對這個int 0x3指令并沒有任何處理,僅僅是把Eip加1跳過它。所以這個int 0x3可以換成任何字節。
int 0x2d和int 0x3生成的異常記錄結(EXCEPTION_RECORD)ExceptionRecord.ExceptionCode都是STATUS_BREAKPOINT(0x80000003),不同是int 0x2d產生的異常的ExceptionRecord.NumberParameters>0且ExceptionRecord.ExceptionInformation對應相應的ServiceClass比如BREAKPOINT_PRINT等。事實上,在內核調試器被掛接后,處理DbgPrint等發送字符給內核調試器不再是通過int 0x2d陷阱服務,而是直接發包。用M$的話說,這樣更安全,因為不用調用KdEnterDebugger和KdExitDebugger。
最后說一下被調試系統和內核調試器之間的通信。被調試系統和內核調試器之間通過串口發數據包進行通信,Com1的IO端口地址為0x3f8,Com2的IO端口地址為0x2f8。在被調試系統預備要向內核調試器發包之前先會調用KdEnterDebugger暫停其它處理器的運行并獲取Com端口自旋鎖(當然,這都是對多處理器而言的),并設置端口標志為保存狀態。發包結束后調用KdExitDebugger恢復。每個包就象網絡上的數據包一樣,包含包頭和具體內容。包頭的格式如下:
typedef struct _KD_PACKET {
ULONG PacketLeader;
USHORT PacketType;
USHORT ByteCount;
ULONG PacketId;
ULONG Checksum;
} KD_PACKET, *PKD_PACKET;
PacketLeader是四個相同字節的標識符標識發來的包,一般的包是0x30303030,控制包是0x69696969,中斷被調試系統的包是0x62626262。每次讀一個字節,連續讀4次來識別出包。中斷系統的包很非凡,包里數據只有0x62626262。包標識符后是包的大小、類型、包ID、檢測碼等,包頭后面就是跟具體的數據。這點和網絡上傳輸的包很相似。還有一些相似的地方比如每發一個包給調試器都會收到一個ACK答復包,以確定調試器是否收到。若收到的是一個RESEND包或者很長時間沒收到回應,則會再發一次。對于向調試器發送輸出字符串、報告SYMBOL情況等的包都是一接收到ACK包就馬上返回,系統恢復執行,系統的表現就是會卡那么短短一下。只有報告狀態的包才會等待內核調試器的每個控制包并完成對應功能,直到發來的包包含繼續執行的命令為止。無論發包還是收包,都會在包的末尾加一個0xaa,表示結束。
現在我們用幾個例子來看看調試流程。
記得我以前問過jiurl為什么WinDBG的單步那么慢(相對softICE),他居然說沒覺得慢?*$&$^$^(&(&(我ft。。。現在可以理解為什么WinDBG的單步和從操作系統正常執行中斷下來為什么那么慢了。單步慢是因為每單步一次除了必要的處理外,還得從串行收發包,怎么能不慢。中斷系統慢是因為只有等到時鐘中斷發生執行到KeUpdateSystemTime后被調試系統才會接受來自WinDBG的中斷包。現在我們研究一下為什么在KiDispatchException里不能下斷點卻可以用單步跟蹤KiDispatchException的原因。假如在KiDispatchException中某處下了斷點,執行到斷點時系統發生異常又重新回到KiDispatchException處,再執行到int 0x3,如此往復造成了死循環,無法不能恢復原來被斷點int 0x3所修改的代碼。但對于int 0x1,因為它的引起是因為EFLAG寄存中TF位被置位,并且每次都自動被復位,所以系統可以被繼續執行而不會死循環。現在我們知道了內部機制,我們就可以調用KdXXX函數實現一個類似WinDBG之類的內核調試器,甚至可以替換KiDebugRoutine(KdpTrap)為自己的函數來自己實現一個功能更強大的調試器,呵呵。

系統與調試器交互的方法除了int 0x3外,還有DbgPrint、DbgPrompt、加載和卸載symbols,它們共同通過調用DebugService獲得服務。
NTSTATUS DebugService(
ULONG ServiceClass,
PVOID Arg1,
PVOID Arg2
)
{
NTSTATUS Status;
__asm {
mov eax, ServiceClass
mov ecx, Arg1
mov edx, Arg2
int 0x2d
int 0x3
mov Status, eax
}
return Status;
}
ServiceClass可以是BEAKPOINT_PRINT(0x1)、BREAKPOINT_PROMPT(0x2)、BREAKPOINT_LOAD_SYMBOLS(0x3)、BREAKPOINT_UNLOAD_SYMBOLS(0x4)。為什么后面要跟個int 0x3,M$的說法是為了和int 0x3共享代碼(我沒弄明白啥意思-_-),因為int 0x2d的陷阱處理程序是做些處理后跳到int 0x3的陷阱處理程序中繼續處理。但事實上對這個int 0x3指令并沒有任何處理,僅僅是把Eip加1跳過它。所以這個int 0x3可以換成任何字節。

int 0x2d和int 0x3生成的異常記錄結(EXCEPTION_RECORD)ExceptionRecord.ExceptionCode都是STATUS_BREAKPOINT(0x80000003),不同是int 0x2d產生的異常的ExceptionRecord.NumberParameters>0且ExceptionRecord.ExceptionInformation對應相應的ServiceClass比如BREAKPOINT_PRINT等。事實上,在內核調試器被掛接后,處理DbgPrint等發送字符給內核調試器不再是通過int 0x2d陷阱服務,而是直接發包。用M$的話說,這樣更安全,因為不用調用KdEnterDebugger和KdExitDebugger。
最后說一下被調試系統和內核調試器之間的通信。被調試系統和內核調試器之間通過串口發數據包進行通信,Com1的IO端口地址為0x3f8,Com2的IO端口地址為0x2f8。在被調試系統預備要向內核調試器發包之前先會調用KdEnterDebugger暫停其它處理器的運行并獲取Com端口自旋鎖(當然,這都是對多處理器而言的),并設置端口標志為保存狀態。發包結束后調用KdExitDebugger恢復。每個包就象網絡上的數據包一樣,包含包頭和具體內容。包頭的格式如下:
typedef struct _KD_PACKET {
ULONG PacketLeader;
USHORT PacketType;
USHORT ByteCount;
ULONG PacketId;
ULONG Checksum;
} KD_PACKET, *PKD_PACKET;
PacketLeader是四個相同字節的標識符標識發來的包,一般的包是0x30303030,控制包是0x69696969,中斷被調試系統的包是0x62626262。每次讀一個字節,連續讀4次來識別出包。中斷系統的包很非凡,包里數據只有0x62626262。包標識符后是包的大小、類型、包ID、檢測碼等,包頭后面就是跟具體的數據。這點和網絡上傳輸的包很相似。還有一些相似的地方比如每發一個包給調試器都會收到一個ACK答復包,以確定調試器是否收到。若收到的是一個RESEND包或者很長時間沒收到回應,則會再發一次。對于向調試器發送輸出字符串、報告SYMBOL情況等的包都是一接收到ACK包就馬上返回,系統恢復執行,系統的表現就是會卡那么短短一下。只有報告狀態的包才會等待內核調試器的每個控制包并完成對應功能,直到發來的包包含繼續執行的命令為止。無論發包還是收包,都會在包的末尾加一個0xaa,表示結束。
現在我們用幾個例子來看看調試流程。
記得我以前問過jiurl為什么WinDBG的單步那么慢(相對softICE),他居然說沒覺得慢?*$&$^$^(&(&(我ft。。。現在可以理解為什么WinDBG的單步和從操作系統正常執行中斷下來為什么那么慢了。單步慢是因為每單步一次除了必要的處理外,還得從串行收發包,怎么能不慢。中斷系統慢是因為只有等到時鐘中斷發生執行到KeUpdateSystemTime后被調試系統才會接受來自WinDBG的中斷包。現在我們研究一下為什么在KiDispatchException里不能下斷點卻可以用單步跟蹤KiDispatchException的原因。假如在KiDispatchException中某處下了斷點,執行到斷點時系統發生異常又重新回到KiDispatchException處,再執行到int 0x3,如此往復造成了死循環,無法不能恢復原來被斷點int 0x3所修改的代碼。但對于int 0x1,因為它的引起是因為EFLAG寄存中TF位被置位,并且每次都自動被復位,所以系統可以被繼續執行而不會死循環。現在我們知道了內部機制,我們就可以調用KdXXX函數實現一個類似WinDBG之類的內核調試器,甚至可以替換KiDebugRoutine(KdpTrap)為自己的函數來自己實現一個功能更強大的調試器,呵呵。

系統與調試器交互的方法除了int 0x3外,還有DbgPrint、DbgPrompt、加載和卸載symbols,它們共同通過調用DebugService獲得服務。
NTSTATUS DebugService(
ULONG ServiceClass,
PVOID Arg1,
PVOID Arg2
)
{
NTSTATUS Status;
__asm {
mov eax, ServiceClass
mov ecx, Arg1
mov edx, Arg2
int 0x2d
int 0x3
mov Status, eax
}
return Status;
}
ServiceClass可以是BEAKPOINT_PRINT(0x1)、BREAKPOINT_PROMPT(0x2)、BREAKPOINT_LOAD_SYMBOLS(0x3)、BREAKPOINT_UNLOAD_SYMBOLS(0x4)。為什么后面要跟個int 0x3,M$的說法是為了和int 0x3共享代碼(我沒弄明白啥意思-_-),因為int 0x2d的陷阱處理程序是做些處理后跳到int 0x3的陷阱處理程序中繼續處理。但事實上對這個int 0x3指令并沒有任何處理,僅僅是把Eip加1跳過它。所以這個int 0x3可以換成任何字節。
int 0x2d和int 0x3生成的異常記錄結(EXCEPTION_RECORD)ExceptionRecord.ExceptionCode都是STATUS_BREAKPOINT(0x80000003),不同是int 0x2d產生的異常的ExceptionRecord.NumberParameters>0且ExceptionRecord.ExceptionInformation對應相應的ServiceClass比如BREAKPOINT_PRINT等。事實上,在內核調試器被掛接后,處理DbgPrint等發送字符給內核調試器不再是通過int 0x2d陷阱服務,而是直接發包。用M$的話說,這樣更安全,因為不用調用KdEnterDebugger和KdExitDebugger。
最后說一下被調試系統和內核調試器之間的通信。被調試系統和內核調試器之間通過串口發數據包進行通信,Com1的IO端口地址為0x3f8,Com2的IO端口地址為0x2f8。在被調試系統預備要向內核調試器發包之前先會調用KdEnterDebugger暫停其它處理器的運行并獲取Com端口自旋鎖(當然,這都是對多處理器而言的),并設置端口標志為保存狀態。發包結束后調用KdExitDebugger恢復。每個包就象網絡上的數據包一樣,包含包頭和具體內容。包頭的格式如下:
typedef struct _KD_PACKET {

ULONG PacketLeader;
USHORT PacketType;
USHORT ByteCount;
ULONG PacketId;
ULONG Checksum;
} KD_PACKET, *PKD_PACKET;
PacketLeader是四個相同字節的標識符標識發來的包,一般的包是0x30303030,控制包是0x69696969,中斷被調試系統的包是0x62626262。每次讀一個字節,連續讀4次來識別出包。中斷系統的包很非凡,包里數據只有0x62626262。包標識符后是包的大小、類型、包ID、檢測碼等,包頭后面就是跟具體的數據。這點和網絡上傳輸的包很相似。還有一些相似的地方比如每發一個包給調試器都會收到一個ACK答復包,以確定調試器是否收到。若收到的是一個RESEND包或者很長時間沒收到回應,則會再發一次。對于向調試器發送輸出字符串、報告SYMBOL情況等的包都是一接收到ACK包就馬上返回,系統恢復執行,系統的表現就是會卡那么短短一下。只有報告狀態的包才會等待內核調試器的每個控制包并完成對應功能,直到發來的包包含繼續執行的命令為止。無論發包還是收包,都會在包的末尾加一個0xaa,表示結束。
現在我們用幾個例子來看看調試流程。
記得我以前問過jiurl為什么WinDBG的單步那么慢(相對softICE),他居然說沒覺得慢?*$&$^$^(&(&(我ft。。。現在可以理解為什么WinDBG的單步和從操作系統正常執行中斷下來為什么那么慢了。單步慢是因為每單步一次除了必要的處理外,還得從串行收發包,怎么能不慢。中斷系統慢是因為只有等到時鐘中斷發生執行到KeUpdateSystemTime后被調試系統才會接受來自WinDBG的中斷包。現在我們研究一下為什么在KiDispatchException里不能下斷點卻可以用單步跟蹤KiDispatchException的原因。假如在KiDispatchException中某處下了斷點,執行到斷點時系統發生異常又重新回到KiDispatchException處,再執行到int 0x3,如此往復造成了死循環,無法不能恢復原來被斷點int 0x3所修改的代碼。但對于int 0x1,因為它的引起是因為EFLAG寄存中TF位被置位,并且每次都自動被復位,所以系統可以被繼續執行而不會死循環。現在我們知道了內部機制,我們就可以調用KdXXX函數實現一個類似WinDBG之類的內核調試器,甚至可以替換KiDebugRoutine(KdpTrap)為自己的函數來自己實現一個功能更強大的調試器,呵呵。

系統與調試器交互的方法除了int 0x3外,還有DbgPrint、DbgPrompt、加載和卸載symbols,它們共同通過調用DebugService獲得服務。
NTSTATUS DebugService(
ULONG ServiceClass,
PVOID Arg1,
PVOID Arg2
)
{
NTSTATUS Status;
__asm {
mov eax, ServiceClass
mov ecx, Arg1
mov edx, Arg2
int 0x2d
int 0x3
mov Status, eax
}
return Status;
}
ServiceClass可以是BEAKPOINT_PRINT(0x1)、BREAKPOINT_PROMPT(0x2)、BREAKPOINT_LOAD_SYMBOLS(0x3)、BREAKPOINT_UNLOAD_SYMBOLS(0x4)。為什么后面要跟個int 0x3,M$的說法是為了和int 0x3共享代碼(我沒弄明白啥意思-_-),因為int 0x2d的陷阱處理程序是做些處理后跳到int 0x3的陷阱處理程序中繼續處理。但事實上對這個int 0x3指令并沒有任何處理,僅僅是把Eip加1跳過它。所以這個int 0x3可以換成任何字節。
int 0x2d和int 0x3生成的異常記錄結(EXCEPTION_RECORD)ExceptionRecord.ExceptionCode都是STATUS_BREAKPOINT(0x80000003),不同是int 0x2d產生的異常的ExceptionRecord.NumberParameters>0且ExceptionRecord.ExceptionInformation對應相應的ServiceClass比如BREAKPOINT_PRINT等。事實上,在內核調試器被掛接后,處理DbgPrint等發送字符給內核調試器不再是通過int 0x2d陷阱服務,而是直接發包。用M$的話說,這樣更安全,因為不用調用KdEnterDebugger和KdExitDebugger。
最后說一下被調試系統和內核調試器之間的通信。被調試系統和內核調試器之間通過串口發數據包進行通信,Com1的IO端口地址為0x3f8,Com2的IO端口地址為0x2f8。在被調試系統預備要向內核調試器發包之前先會調用KdEnterDebugger暫停其它處理器的運行并獲取Com端口自旋鎖(當然,這都是對多處理器而言的),并設置端口標志為保存狀態。發包結束后調用KdExitDebugger恢復。每個包就象網絡上的數據包一樣,包含包頭和具體內容。包頭的格式如下:
typedef struct _KD_PACKET {
ULONG PacketLeader;
USHORT PacketType;
USHORT ByteCount;
ULONG PacketId;
ULONG Checksum;
} KD_PACKET, *PKD_PACKET;
PacketLeader是四個相同字節的標識符標識發來的包,一般的包是0x30303030,控制包是0x69696969,中斷被調試系統的包是0x62626262。每次讀一個字節,連續讀4次來識別出包。中斷系統的包很非凡,包里數據只有0x62626262。包標識符后是包的大小、類型、包ID、檢測碼等,包頭后面就是跟具體的數據。這點和網絡上傳輸的包很相似。還有一些相似的地方比如每發一個包給調試器都會收到一個ACK答復包,以確定調試器是否收到。若收到的是一個RESEND包或者很長時間沒收到回應,則會再發一次。對于向調試器發送輸出字符串、報告SYMBOL情況等的包都是一接收到ACK包就馬上返回,系統恢復執行,系統的表現就是會卡那么短短一下。只有報告狀態的包才會等待內核調試器的每個控制包并完成對應功能,直到發來的包包含繼續執行的命令為止。無論發包還是收包,都會在包的末尾加一個0xaa,表示結束。
現在我們用幾個例子來看看調試流程。
記得我以前問過jiurl為什么WinDBG的單步那么慢(相對softICE),他居然說沒覺得慢?*$&$^$^(&(&(我ft。。。現在可以理解為什么WinDBG的單步和從操作系統正常執行中斷下來為什么那么慢了。單步慢是因為每單步一次除了必要的處理外,還得從串行收發包,怎么能不慢。中斷系統慢是因為只有等到時鐘中斷發生執行到KeUpdateSystemTime后被調試系統才會接受來自WinDBG的中斷包。現在我們研究一下為什么在KiDispatchException里不能下斷點卻可以用單步跟蹤KiDispatchException的原因。假如在KiDispatchException中某處下了斷點,執行到斷點時系統發生異常又重新回到KiDispatchException處,再執行到int 0x3,如此往復造成了死循環,無法不能恢復原來被斷點int 0x3所修改的代碼。但對于int 0x1,因為它的引起是因為EFLAG寄存中TF位被置位,并且每次都自動被復位,所以系統可以被繼續執行而不會死循環。現在我們知道了內部機制,我們就可以調用KdXXX函數實現一個類似WinDBG之類的內核調試器,甚至可以替換KiDebugRoutine(KdpTrap)為自己的函數來自己實現一個功能更強大的調試器,呵呵。進入討論組討論。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 兴业县| 邹平县| 万荣县| 都匀市| 衢州市| 年辖:市辖区| 南和县| 东乌珠穆沁旗| 农安县| 达拉特旗| 虞城县| 项城市| 东乌珠穆沁旗| 沂南县| 莱阳市| 宝丰县| 祁连县| 修文县| 元朗区| 来安县| 青田县| 宝鸡市| 呈贡县| 亳州市| 澳门| 陆河县| 玛纳斯县| 澄城县| 兴和县| 松溪县| 宜君县| 浦北县| 祁阳县| 石家庄市| 胶南市| 高阳县| 新蔡县| 凤翔县| 张家川| 宿松县| 天峻县|