分享
 
 
 

Windows内核调试器原理浅析

王朝system·作者佚名  2008-05-19
窄屏简体版  字體: |||超大  

前段时间忽然对内核调试器实现原来发生了兴趣,于是简单分析了一下当前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

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有