精华内容
下载资源
问答
  • windows内核调试模式的开启

    千次阅读 2011-08-27 13:33:21
    默认情况下,windows是不开启内核调试模式的。可以通过以下命令开启: bcdedit /debug ON 重启计算机器后windows内核就处于调试模式了; 在此模式下,有下安全性要求极高的软件是用不了的,如某些银行的网银系统...

    默认情况下,windows是不开启内核调试模式的。可以通过以下命令开启:

    bcdedit /debug ON

    重启计算机器后windows内核就处于调试模式了;

    在此模式下,有下安全性要求极高的软件是用不了的,如某些银行的网银系统。

    我们可以通过bcdedit /debug OFF 来关闭内核调试模式,同样需要重启计算机。

    展开全文
  • WINDOWS内核调试经典教程 ,真的很经典
  • Windows内核调试初探

    千次阅读 2018-10-18 15:04:03
    本文将对Windows平台下的内核调试进行简单探讨 1. 准备和设置虚拟机 为了进行内核调试,我们准备一个虚拟机(此处为Win7 x64)。 在 VMVare 中进入该虚拟机的设置,在硬件中添加 串行端口(选择使用命名的管道 “\....

    本文将对Windows平台下的内核调试进行简单探讨

    1. 准备和设置虚拟机

    为了进行内核调试,我们准备一个虚拟机(此处为Win7 x64)。
    在 VMVare 中进入该虚拟机的设置,在硬件中添加 串行端口(选择使用命名的管道 “\.\pipe\com_1”)
    此处必须确保硬件下有 “串行端口” 项,而不是 “串行端口 2” (若为 “串行端口 2”,将打印机和该端口删除,重新添加 串行端口)

    2. 命令行调起Windbg连接虚拟机进行调试

    进入windbg.exe目录
    C:\Program Files (x86)\Debugging Tools for Windows (x86)>
    C:\Program Files (x86)\Debugging Tools for Windows (x86)>windbg -k com:pipe,port=\.\pipe\com_1
    当调起windbg.exe并且出现 Kernel Debugger connection established. 提示时,可以按Ctrl+Break,此时可以暂且认为是Windows系统的最早的内核调试起点,有兴趣可以解 Phare One 过程(http://www.theitcareer.com/site/?p=28)

    3.1 查看记事本信息

    在虚拟机上打开记事本(notepad.exe),点击 帮助->关于记事本

    3.2 在 windbg 中观察这一过程

    在 Windbg 中 Break(Ctrl+Break)
    此时要加载microsoft的符号(详见之前的介绍文章 WinDbg工具)
    Ctrl+S
    srvf:\mysymbolshttp://msdl.microsoft.com/download/symbols;cache*f:\mysymbols

    3.2.1 找出记事本进程

    枚举所有进程,在 windbg 中输入以下语句

    !process 0 0

    结果截取一部分如下:

    PROCESS fffffa801a8b1b30
    SessionId: 1 Cid: 025c Peb: fffdf000 ParentCid: 0b28
    DirBase: 47098000 ObjectTable: 00000000 HandleCount: 0.
    Image: et.exe
    PROCESS fffffa801abaf130
    SessionId: 1 Cid: 062c Peb: 7efdf000 ParentCid: 025c
    DirBase: 3eeb3000 ObjectTable: fffff8a001fd62c0 HandleCount: 6.
    Image: kcrashdumper.exe
    PROCESS fffffa801abf5530
    SessionId: 1 Cid: 0980 Peb: 7efdf000 ParentCid: 04d0
    DirBase: 39fb5000 ObjectTable: fffff8a0025eca40 HandleCount: 68.
    Image: notepad.exe
    PROCESS fffffa8018e3c890
    SessionId: 1 Cid: 08d0 Peb: 7fffffdc000 ParentCid: 03b8
    DirBase: 385e7000 ObjectTable: fffff8a002d1e2e0 HandleCount: 82.
    Image: taskeng.exe

    找出记事本进程的地址,这里我们看到是 0xfffffa801abf5530(注意前面的0x也是需要的),此处为方便,该地址记为

    AOTP(address_of_target_process)

    (内核里的地址要比之前我们看到的长很多呢,内核这边地址均高于 fffff800 00000000)

    3.2.2 将记事本进程放到内存中

    因为操作系统会将不常用的东西放在虚拟内存中,而将常用的部分放在内存,CPU无法对虚拟内存的东西进行直接访问

    输入以下命令将进程内容放回到内存中(AOTP见上文):

    .process /p AOTP

    结果如下:

    Implicit process is now fffffa80`1abf5530
    .cache forcedecodeuser done

    查看记事本进程的堆栈及其它信息
    输入以下命令:

    !process AOTP

    结果如下:

    PROCESS fffffa801abf5530
    SessionId: 1 Cid: 0980 Peb: 7efdf000 ParentCid: 04d0
    DirBase: 39fb5000 ObjectTable: fffff8a0025eca40 HandleCount: 68.
    Image: notepad.exe
    VadRoot fffffa801a8418f0 Vads 75 Clone 0 Private 277. Modified 2. Locked 0.
    DeviceMap fffff8a0038661e0
    Token fffff8a002536060
    ElapsedTime 02:57:21.333
    UserTime 00:00:00.000
    KernelTime 00:00:00.000
    QuotaPoolUsage[PagedPool] 0
    QuotaPoolUsage[NonPagedPool] 0
    Working Set Sizes (now,min,max) (2292, 50, 345) (9168KB, 200KB, 1380KB)
    PeakWorkingSetSize 2293
    VirtualSize 83 Mb
    PeakVirtualSize 83 Mb
    PageFaultCount 2473
    MemoryPriority BACKGROUND
    BasePriority 8
    CommitCharge 352
    .
    THREAD fffffa801abf17a0 Cid 0980.0520 Teb: 000000007efdb000 Win32Thread: fffff900c2c8dc30 WAIT: (WrUserRequest) UserMode Non-Alertable
    fffffa801a8442e0 SynchronizationEvent
    Not impersonating
    DeviceMap fffff8a0038661e0
    Owning Process fffffa801abf5530 Image: notepad.exe
    Attached Process N/A Image: N/A
    Wait Start TickCount 82166 Ticks: 2 (0:00:00:00.031)
    Context Switch Count 17053 LargeStack
    UserTime 00:00:00.000
    KernelTime 00:00:00.156
    Win32 Start Address 0x0000000000c73689
    Stack Init fffff88003912c70 Current fffff88003912730
    Base fffff88003913000 Limit fffff88003909000 Call 0
    Priority 12 BasePriority 8 UnusualBoost 0 ForegroundBoost 2 IoPriority 2 PagePriority 5
    Child-SP RetAddr : Args to Child : Call Site
    fffff88003912770 fffff80003eea992 : fffff900c0842f30 fffffa801abf17a0 0000000000001101 0000000000000008 : nt!KiSwapContext+0x7a
    fffff880039128b0 fffff80003eed1af : 0000000000000000 000000007efdb000 fffff88000000000 0000000000000000 : nt!KiCommitThreadWait+0x1d2
    fffff88003912940 fffff960000db837 : fffff900c2c8dc00 000000000000000d 0000000001040001 fffff96000066500 : nt!KeWaitForSingleObject+0x19f
    fffff880039129e0 fffff960000db8d1 : 0000000000000000 0000000000000000 0000000000000001 0000000000000000 : win32k!xxxRealSleepThread+0x257
    fffff88003912a80 fffff960000ee4d2 : 0000000075f6f5be fffff88003912b60 fffff960000ee48c fffff960000ee48c : win32k!xxxSleepThread+0x59
    fffff88003912ab0 fffff80003ee48d3 : fffffa801abf17a0 0000000000000000 000000000033fb90 0000000000000020 : win32k!NtUserWaitMessage+0x46
    fffff88003912ae0 0000000074e52e09 : 0000000074e52dbf 0000000075f50735 0000000074ec0023 0000000000000246 : nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ fffff88003912ae0) 000000000022e4a8 0000000074e52dbf : 0000000075f50735 0000000074ec0023 0000000000000246 000000000012f980 : 0x74e52e09 000000000022e4b0 0000000075f50735 : 0000000074ec0023 0000000000000246 000000000012f980 000000000022002b : 0x74e52dbf 000000000022e4b8 0000000074ec0023 : 0000000000000246 000000000012f980 000000000022002b 0000000000390008 : 0x75f50735 000000000022e4c0 0000000000000246 : 000000000012f980 000000000022002b 0000000000390008 0000000000000000 : 0x74ec0023 000000000022e4c8 000000000012f980 : 000000000022002b 0000000000390008 0000000000000000 0000000000000000 : 0x246 000000000022e4d0 000000000022002b : 0000000000390008 0000000000000000 0000000000000000 000000000012fa68 : 0x12f980 000000000022e4d8 0000000000390008 : 0000000000000000 0000000000000000 000000000012fa68 0000000000000030 : 0x22002b 000000000022e4e0 0000000000000000 : 0000000000000000 000000000012fa68 0000000000000030 00000000`00000000 : 0x390008

    注意观察以下两行

    fffff88003912ae0 0000000074e52e09 : 0000000074e52dbf 0000000075f50735 0000000074ec0023 0000000000000246 : nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ fffff88003912ae0) 000000000022e4a8 0000000074e52dbf : 0000000075f50735 0000000074ec0023 0000000000000246 00000000`0012f980 : 0x74e52e09

    这里是从用户态的代码转至内核态的代码,这里用户态的看不到是因为我们没有加载记事本的 pdb。

    4. 结语

    至此,我们只是简单的看了一下记事本的部分堆栈,算是内核调试的一个小开端。若我们有记事本的pdb,就可以很自然地下断点,开始内核模式下的用户态调试了。

    展开全文
  • 掌握Windows内核调试技术是学习与研究Windows内核的基础,调试Windows内核的方式大致分为两种: (1)通过Windbg工具在Windows系统运行之初连接到Windows内核,连接成功之后便可以调试,此时即可以调试Windows...

    掌握Windows内核调试技术是学习与研究Windows内核的基础,调试Windows内核的方式大致分为两种:

    (1)通过Windbg工具在Windows系统运行之初连接到Windows内核,连接成功之后便可以调试,此时即可以调试Windows内核启动过程,又可以在Windows启动之后调试某内核组件或应用程序。或使用Windbg的Kernel debugging of the local mechine功能,在Windows系统完全启动之后,调试Windows内核组件或应用程序。这种方式需要配置Windows系统以DEBUG模式启动。

    (2)通过Bochs来运行Windows系统,可以在Windows运行的任意时刻调试任意信息,但最大的缺点就是,这种方式目前不支持使用Windows Symbols,与第一种方式相比,汇编代码读起来并不直观。

    本文介绍方式(1)即通过Windbg工具调试Windows内核。

    1、Windbg工具简介及获取方式

    Windbg工具是微软团队开发和公布的一款专门用于调试Windows内核及应用程序的调试器,既支持内核级(Ring0)调试,也支持应用程序级(Ring3)调试。如今的Windows系统调试器有多种,出了Windbg以外,还有Olldbg、Immunity Debugger等。Olldbg和Immunity Debugger不支持内核级调试。

    Windbg工具包含在Windows Driver Kit(WDK,Windows驱动开发包)中,当然也可以单独下载。

    (1)如果要通过WDK方式获取的话,Windows XP及之前的环境只要下载WDK即可,整个资源大约300M~500M,但是在Windows Vista/Windows 7等系统之后,微软将WDK与Visual Studio环境集成到一起,因此整个下载资源大约6.0G~7.0G。由于Windows XP系统不再支持,并且笔者的实验环境为Windows 7系统,在后续的文章中也会涉及到Windows驱动开发,所以笔者建议下载WDK与VS集成包,虽然资源比较大。

    (2)如果只是暂时学习Windows内核调试的话,可以单独下载Windbg工具,整个资源大约150M。

    以上资源皆可以在MSDN上获取,获取链接为:http://msdn.microsoft.com/zh-cn/windows/hardware/hh852365.aspx

    2、Windows Symbols简介及获取方式

    Windows Symbols是微软公开的Windows系统符号表,使用Windbg调试WIndows内核和应用程序时可以很直观地看到部分变量名称、结构体结构、函数名称等。

    Windows Symbols可以直接在MSDN上获取,其获取页面与上述给出的获取Windbg工具的页面链接相同。

    但是笔者不建议直接下载并安装Windows Symbols,因为在使用Windbg工具调试时,可以根据需要即时获取相关模块的符号信息(如果网络正常的话)。

    3、Windows内核调试方法

    通过虚拟机调试Windows系统是种常见的方式,即首先搭建虚拟机环境,即将Windows系统安装在虚拟机中,然后配置虚拟机Windows系统的启动模式为DEBUG模式,并且配置好Windbg工具与虚拟机Windows系统之间的通信通道,在Windbg进入等待调试通信的时候,重启虚拟机Windows系统,双方的调试链接建立以后,即可使用Windbg工具调试/控制Windows内核,详细步骤如下:

    (1)配置虚拟机Windows系统以DEBUG模式启动。

    进入虚拟机Windows系统,以管理员身份运行cmd.exe,执行以下命令:

    bcdedit /debug on    //启用Windows内核调试支持

    bcdedit /bootdebug on  //启用应用程序的启动过程调试支持

    关于bcdedit命令的详细信息,可以在MSDN中了解:http://msdn.microsoft.com/zh-cn/library/dn653986(v=vs.85).aspx

    上述方式仅在Windows Vista/7系统及以后版本的Windows系统中有效,如不做特殊说明,笔者的实验环境默认为VirtualBox/Windows 7系统。

    对于Windows XP系统的配置,请自行GOOGLE。

    展开全文
  • 使用WinDbg进行Windows内核调试的详细过程,安装虚拟机。比楚狂人介绍的稍详细。巨崇拜楚狂人
  • Windows内核调试器 - 简介

    千次阅读 2016-03-24 20:27:12
    Windows内核调试器 - 原理 WinDbg    WinDBG和用户调试器一点很大不同是内核调试器在一台机器上启动,通过串口调试另一个相联系的以Debug方式启动的系统,这个系统可以是虚拟机上的系统,也可以是另一台...


    转自 : 点击打开链接

    Windows内核调试器 - 原理

    WinDbg
        
        WinDBG用户调试器一点很大不同是内核调试器在一台机器上启动,通过串口调试另一个相联系的以Debug方式启动的系统,这个系统可以是虚拟机上的系统,也可以是另一台机器上的系统(这只是微软推荐和实现的方法,其实象SoftIce这类内核调试器可以实现单机调试)。很多人认为主要功能都是在WinDBG里实现,事实上并不是那么一回事,windows已经把内核调试的机制集成进了内核,WinDBG、kd之类的内核调试器要做的仅仅是通过串行发送特定格式数据包来进行联系,比如中断系统、下断点、显示内存数据等等。然后把收到的数据包经过WinDBG处理显示出来。    

        在进一步介绍WinDBG之前,先介绍两个函数:KdpTraceKdpStub,我在《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)为自己的函数来自己实现一个功能更强大的调试器,呵呵。


                    SoftICE

        SoftICE的原理和WinDBG完全不一样。它通过替换正常系统中的中断处理程序来获得系统的控制权,也正因为这样它才能够实现单机调试。它的功能实现方法很底层,很少依赖与windows给的接口函数,大部分功能的实现都是靠IO端口读写等来完成的。

        SoftICE替换了IDT表中以下的中断(陷阱)处理程序:

        0x1:    单步陷阱处理程序
        0x2:    NMI可屏蔽中断
        0x3:    调试陷阱处理程序
        0x6:    无效操作码陷阱处理程序
        0xb:    段不存在陷阱处理程序
        0xc:    堆栈错误陷阱处理程序
        0xd:    一般保护性错误陷阱处理程序
        0xe:    页面错误陷阱处理程序
        0x2d:    调试服务陷阱处理程序
        0x2e:    系统服务陷阱处理程序
        0x31:    8042键盘控制器中断处理程序
        0x33:    串口2(Com2)中断处理程序
        0x34:    串口1(Com1)中断处理程序
        0x37:    并口中断处理程序
        0x3c:    PS/2鼠标中断处理程序
        0x41:    未使用

        (这是在PIC系统上更换的中断。如果是APIC系统的话更换的中断号有不同,但同样是更换这些中断处理程序)

        其中关键是替换了0x3 调试陷阱处理程序和0x31 i8042键盘中断处理驱动程序(键盘是由i8042芯片控制的),SoftICE从这两个地方获取系统的控制权。
        
        启动softICE服务后SoftICE除了更换了IDT里的处理程序,还有几点重要的,一是HOOK了i8042prt.sys里的READ_PORT_UCHAR函数,因为在对0x60端口读后,会改变0x64端口对应控制寄存器的状态。所以在SoftICE的键盘中断控制程序读了0x60端口后并返回控制权给正常的键盘中断控制程序后,不要让它再读一次。还有就是把物理内存前1MB的地址空间通过调用MmMapIoSpace映射到虚拟的地址空间里,里面包括显存物理地址,以后重画屏幕就通过修改映射到虚拟地址空间的这段显存内容就行了。

        如果显示模式彩色模式,那么显存起始地址是0xb8000,CRT索引寄存器端口0x3d4,CRT数据寄存器端口0x3d5。如果显示模式是单色模式,那么显存起始地址是0xb0000,CRT索引寄存器端口0x3b4,CRT数据寄存器端口0x3b5。首先写索引寄存器选择要进行设置的显示控制内部寄存器之一(r0-r17),然后将参数写到其数据寄存器端口。

        i8042键盘控制器中断控制驱动程序在每按下一个键和弹起一个键都会被触发。SoftICE在HOOK了正常的键盘中断控制程序获得系统控制权后,首先从0x60端口读出按下键的扫描码然后向0x20端口发送通用EOI(0x20)表示中断已结束,如果没有按下激活热键(ctrl+d),则返回正常键盘中断处理程序。如果是按下热键则会判断控制台(就是那个等待输入命令的显示代码的黑色屏幕)是否被激活,未被激活的话则先激活。然后设置IRQ1键盘中断的优先级为最高,同时设置两个8259A中断控制器里的中断屏蔽寄存器(向0x21和0xa1发中断掩码,要屏蔽哪个中断就把哪一位设为1),只允许IRQ1(键盘中断)、IRQ2(中断控制器2级联中断,因为PS/2鼠标中断是归8259A-2中断控制器管的,只有开放IRQ2才能响应来自8259A-2管理的中断)、IRQ12(PS/2鼠标中断,如果有的话),使系统这时只响应这3个中断。新的键盘和鼠标中断处理程序会建立一个缓冲区,保存一定数量的输入扫描信息。当前面的工作都完成后会进入一段循环代码,负责处理键盘和鼠标输入的扫描码缓冲区,同时不断地更新显存的映射地址缓冲区重画屏幕(这段循环代码和WinDBG里循环等待从串口发来的包的原理是一样的,都是在后台循环等待用户的命令)。这段循环代码是在激活控制台的例程里调用的,也就是说当控制台已被激活的话正常流程不会再次进入这段循环代码的(废话,再进入系统不就死循环了)。当有一个新的键按下时,都会重新调用一遍键盘中断处理程序,因为控制台已激活,所以它只是简单地更新键盘输入缓冲区内容然后iret返回。它并不会返回正常的键盘中断处理程序,因为那样会交出控制权(想证明这点也很简单,在SoftICE里断正常的键盘中断处理程序,然后g,1秒后在这里断下,这是我们可以F10,如果SoftICE会把控制权交给正常的键盘中断处理程序的话,在这里早就发生死循环了)。鼠标中断驱动也是一样。这个时候实际iret返回到的还是那段循环代码里面,所以被调试的代码并不会被执行,除非按下了F10之类的键,它会指示退出循环返回最开始时的中断处理程序,然后再iret返回最开始中断的地方。当然,因为设置了EFLAG里的TF位,执行了一个指令又会通过单步的处理程序进入那段循环的代码。

        而处理int 0x3也差不多,若没有激活控制台则先激活并屏蔽除了键盘、鼠标及8259A-2中断控制器外的所有中断,然后进入那段循环代码。

        作为对比同样来看一下在SoftICE里处理int 0x3和单步的过程。当执行到int 0x3时,激活控制台并屏蔽中断,然后将int 0x3指令前后范围的指令反汇编并写入显存映射地址空间,并把最新的寄存器值也写进去,最后在后台循环等待键盘输入命令。当命令是F10时,设置好EFLAG的TF位,清除8259A中断控制器里的中断屏蔽寄存器,开放所有中断,将控制台清除,从循环代码中返回新键盘(或int 0x3)中断处理程序,然后再返回到正常键盘(或int 0x3)中断处理程序,由这里iret到被中断代码处执行。执行了一个指令后因为发生单步异常又进入后台循环代码。

        SoftICE里的单步比WinDBG要快得多的原因很简单,SoftICE只需要把反汇编出来的代码和数据经过简单处理再写入显存映射地址缓冲区里刷新屏幕就可以继续执行了,省略了串行的发包收包,怎么会不快。而中断系统更快,按下键中断就会发生,根本不用象WinDBG等时钟中断才能把系统断下来。


    展开全文
  • Windows内核调试器原理浅析

    千次阅读 2013-04-17 17:07:31
    前段时间忽然对内核调试器实现原来发生了兴趣,于是简单分析了一下当前windows下主流内核调试器原理,并模仿原理自己也写了个极其简单的调试器:) WinDBG WinDBG和用户调试器一点很大不同是内核调试器在一台...
  • 调试环境搭建 1、Microsoft Visual Studio 2010 2、Windbg 3、Visual DDK 4、VMware Workstation 依次从官网下载上述软件并安装到本地 安装Visual DDK 的过程中会进行相应的配置,包括自动识别已安装的...
  • Windows内核调试:符号文件和Windbg的安装与环境配置 flyingstarsoul 2007-9-21 内核调试: 内核调试可以帮助我们查看内核的内部数据结构,跟踪内核中的函数,从而探查Windows的内部机理。内核调试工具有Microsoft的...
  • 由于学习需要windows内核与驱动调试环境,参阅了网上一些前人留下的资料,终于搭建成功。 自己走过的一些弯路:首先曾尝试使用Driver Studio中的SoftICE工具加虚拟机软件VMware来做,不知是我哪儿的参数设置不对...
  • 1. 远程调试的三种连接方式:串行口,1394和USB 2.0 串口分为9针和25针,是最基本的通信方式,所有的内核通信都支持串口通信。 1394又成为火线,是一种高性能的串行总线通信标准。使用1394进行通信时,目标系统和...
  • 虚拟数据库隐藏 Windows 内核模式驱动程序使用 Intel 的硬件虚拟化来挂钩 MSR_LSTAR(系统调用处理程序)。 当前绕过 Windows 8.1 上的 PatchGuard。
  • 调试windows内核

    千次阅读 2011-07-03 11:14:17
    2 设置windows xp调试  打开系统安装盘,在文件夹选项中设置为显示所有文件,不隐藏系统保护的文件,然后就可以看到一个boot.ini文件   boot.ini 的文件内容一般如下    [boot loader]
  • Windows内核模式和用户模式

    千次阅读 2015-03-11 16:34:13
    运行 Windows 的计算机中的处理器有两个不同模式:“用户模式”和“内核模式”。根据处理器上运行的代码的类型,处理器在两个模式之间切换。应用程序在用户模式下运行,核心操作系统组件在内核模式下运行。多个驱动...
  • 配置Vmware 添加一个串行端口 使用命名管道,按照下图设置好 配置系统调试模式 winxp 下面是xp系统的调试设置,目的就为了启动能够进入调试模式 win7 注意win7的设置方法有所不同 配置调试器 Windbg 需要配置快捷...
  • Windows操作系统的内核调试方法

    千次阅读 2011-03-12 12:29:00
    当我们想要调试操作系统时,可以修改操作系统启动参数,方法有二,一是在系统启动时F8下,选择调试模式,二是,在启动后,修改启动配置参数。Windows xp /2003 使用 boot.ini 存储启动参数,Windows Vista / 7使用...
  • Windows内核开发调试环境配置详解

    千次阅读 2011-08-31 16:27:51
    应用程序使用开发包SDK,类似的,内核编程使用"Windows Driver Kit",简称WDK。WDK已经自带所有需要的头文件、库、C/C++语言及汇编语言的编译器和链接器 如何获取WDK 参见 http://msdn.microsof
  • flyingstarsoul 2007-9-21      内核调试内核调试可以帮助我们查看内核的内部数据结构,跟踪内核中的函数,从而探查Windows的内部机理。内核调试工具有Microsoft的Windows调试工具箱、Compuware NuM
  • 使用WinDbg内核调试

    2009-08-18 17:44:31
    本文的重点是集成内核模式和用户态模式的图形化调试器WinDbg,目标是给予一个已经有其他调试工具使用经验的开发者足够信息,使其能通过参考WINDOWS调试工具的帮助文件进行内核调试
  • WINDOWS有两种访问模式:用户模式内核模式。用户模式下的代码不可以直接访问系统空间的数据,也不可以直接调用系统空间中的任何函数,否则会导致保护性错误。但用户程序可以通过调用系统服务来间接访问系统空间的...
  • 标 题: 【原创】某驱动的内核调试检测学习内核调试引擎加载机制 作 者: 毁灭 时 间: 2014-03-29,02:13:41 链 接: http://bbs.pediy.com/showthread.php?t=186091 作者:Tiany QQ:304400230 如果大家调试...
  • windows内核模式下隐藏进程

    千次阅读 2012-12-19 17:07:04
    1、在内核模式下,系统为每个进程维护了一个EPROCESS结构体,系统所有的进程是通过EPROCESS结构体中的一个ActiveProcessLinks指向的双端链表连接起来的,通过winDBG内核调试工具就可以发现并获取其相对于EPROCESS...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 36,997
精华内容 14,798
关键字:

windows内核调试模式