第一个内核程序
刚刚搭建完调试程序,应该趁热打铁,来一个程序调试调试。调试程序是参照《寒江独钓——Windows内核编程》敲的,算是一次整理加深印象。
一、准备工作:
1、WDK
可以在:http://www.microsoft.com/en-us/download/details.aspx?id=11800,下载到,注意最好选择完整安装
2、Windbg
完整晚装WDK后可以在其安装目录的\7600.16385.1\Debuggers下找到。
3、DebugView
可以在网上搜索到资源,用于查看内核程序的输出。
4、srvinstw
软件也可以在网上搜索到资源,用于win系统的服务安装与移除。
以上为调试主要用到的工具,至于调试的环境搭建已经写有记录的文章,这里就不在重复了,一下直接开始进行内核程序的调试。
二、第一个内核程序编译与运行:
2.1 采用的代码:
#include "ntddk.h"
//提供一个Unload函数只是为了让这个程序能动态卸载,方便调试 VOID DriverUnload(PDRIVER_OBJECT driver) { DbgPrint("first: our driver is unloading...\r\n"); } //DriverEntry,入口函数,相当于main函数 NTSTATUS DriverEntry( PDRIVER_OBJECT driver, PUNICODE_STRING reg_path ) { //这是内核模块的入口,可以在这里写入我们想写的东西
//在这里就打印一句话
DbgPrint("first: Hello,my salary!\r\n");
//设置一个卸载函数,便于函数退出 driver->DriverUnload = DriverUnload; return STATUS_SUCCESS; }
以上代码可以使用一般的文本编辑器编写保存为.c文件即可,这里保存问test1.c。
2.2 编译一个工程:
写完.c文件源代码以后,还需要其它的两个文件才能进行编译,分别是makefile和SOURCES文件(注:这两个文件的格式是所有格式,也就是说没有后缀,名字不要改)。其中在下载WDK的时候其中附带了许多例子,我们可以从例子中找到这两个文件拷贝出来进行少许的修改就可以。我是从WDK路径下的\7600.16385.1\src\storage\filters\diskperf,文件假中拷贝的这两个文件。
makefile文件内容如下:
!IF 0 Copyright (C) Microsoft Corporation, 1999 - 2008 Module Name: makefile. !ENDIF # # DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source # file to this component. This file merely indirects to the real make file # that is shared by all the components of Windows NT # # # if building in a DDK environment # !IF defined(DDK_TARGET_OS) # # ensure that said build environment is at least Windows XP # 0x500 == Windows 2000 # 0x501 == Windows XP # 0x502 == Windows .NET # ! IF defined(_NT_TARGET_VERSION) && $(_NT_TARGET_VERSION)>=0x501 ! INCLUDE $(NTMAKEENV)\makefile.def ! ELSE ! message BUILDMSG: Warning : The sample "$(MAKEDIR)" is not valid for the current OS target. ! ENDIF !ELSE # # not a DDK environment, probably RAZZLE, so build # ! INCLUDE $(NTMAKEENV)\makefile.def !ENDIF
SOURCES文件内容:
TARGETNAME=test1
TARGETTYPE=DRIVER
TARGETPATH=obj
SOURCES=test1.c
其中TARGETNAME表示名字,编译出来后,模块的名字为test1.sys;SOURCES表示要编译的.c文件(对于初学者,就像我这样的,必须要注意一点,不要加入.h文件,因为.h文件是被包含在.c文件中编译的),若果.c文件有多个,请用空格隔开。现在我们已经有test1.c,makefile,SOURCES三个文件,新建立一个文件夹"test1",将这三个文件放到这个文件夹中。
下面就开始编译工程:如下图所示在【开始】菜单中打开WDK的build环境配置,Checked Build是用于编译调试程序,而Free Build是用于编译正式发布的程序。
在上图菜单中选择X86 Checked Build Enviroment,弹出控制台界面,如下图所示,cd到所建立的test1文件夹下,然后键入build即可开始进行编译。
编译结束后,test1出现在test1\objchk_wxp_x86\i386目录下。这个文件并不能像普通的exe文件一样可以直接双击执行,需要一个安装工具进行安装。下面将讲解如何进行安装。
2.3 安装与运行:
可以用刚开始提到的srvinstw小工具进行服务的安装,安装过程如下:
【1】打开工具,选择“安装服务”,单击“下一步”
【2】在选择及其类型界面中选择“本地机器”,我是将该软件考到虚拟机上了,就选了该项,当然可以试试远程机器安装方式。点击“下一步”
【3】在服务名称录入框中任意输入一个服务的名称,我这里是test1。点击“下一步”
【4】在输入程序路径对话框中,输入刚刚编译生成的test1.sys的路径,我的路径如下图所示。点击“下一步”
【5】安装的种类选择“设备驱动”。点击“下一步”
【6】NT驱动器名称可以不写,直接点击“下一步”
【7】启动类型选择“手动”。单击“下一步”
【8】可以看到服务已经安装成功
一下就是运行安装好的服务了,由于内核输出我们在应用层是无法看到的,这里就得需要借助DebugView,这样就可以看到内核程序的输出了。注意再运行程序之前应该先启动DebugView,并且在DebugView的菜单栏中设置查看内核输出:打开菜单栏中的【Capture】选项——》将下拉框中的【Capture Kernel】选项勾上即可。
再者就打开cmd控制台,键入命令:net start test1 ,启动程序(注意:test1为安装服务时给服务取的名字),可以看到服务已经成功启动,并且DebugView中显示了输出结果。
可以运行命令:net stop test1停止服务。还可以使用srvinstw工具将服务remove掉(注:在选择删除的服务名界面中,要将对话框左下角的【包括设备驱动器】的复选框勾上)。
三、实战调试:
【1】修改源程序,手工设置断点
说到调试,设置断点进行单步调试是常有的事。使用的test1这个驱动实际上什么也没有做,只是进行打印输出,所以加载之后DriverEntry会被执行一次,之后就只有卸载时会执行DriverUnload了。这样就没什么可以调试的了,为了看到效果,只能再程序入口函数处,即DriverEntry中设置一个断点。
在驱动加载之前,设置断点是不方便的。参考课本加入一个断点。程序如下:
#include "ntddk.h" //提供一个Unload函数只是为了让这个程序能动态卸载,方便调试 VOID DriverUnload(PDRIVER_OBJECT driver) { DbgPrint("first: our driver is unloading...\r\n"); } //DriverEntry,入口函数,相当于main函数 NTSTATUS DriverEntry( PDRIVER_OBJECT driver, PUNICODE_STRING reg_path ) { #if DBG _asm int 3 //breakpoint #endif //这是内核模块的入口,可以在这里写入我们想写的东西 //在这里就打印一句话 DbgPrint("first: Hello,my salary!\r\n"); //设置一个卸载函数,便于函数退出 driver->DriverUnload = DriverUnload; return STATUS_SUCCESS; }
代码中的 int 3 是一句汇编指令,执行到这条指令的时候程序就会断下来,等于设置了一个手工断点。但是这样的代码如果不是调试运行的话就会出现蓝屏,所以加上了宏DBG测试。
【2】对修改后的源代码进行编译,编译步骤跟第二节中描述的步奏一直,当然这次只需要build一下就可以了。
【3】以调试运行的模式启动虚拟机(被调试机)。
【4】将编译生成的test1.sys文件拷贝到虚拟机中,然后根据第二节中的步骤进行安装。(这里需要注意的是,服务名称不能重名,所以这次安装可以改一个服务名称,当然也可以将第二节中进行测试的服务先卸载掉然后重启电脑再装一次。)
【5】在调试及也就是真是物理机中运行Windbg,并且进行如下设置:
Windbg的符号表路径,在【File】菜单下的【Symbol File Path】的后面加上驱动程序的符号表,注意用分号(;)隔开,修改后的内容如下。
srv*c:\symbols*http://msdl.microsoft.com/download/symbols;E:\WinDDK\test1\objchk_wxp_x86\i386
源文件路径设置,在【File】菜单下的【Source File Path】中写入源文件路径,我的是:
E:\WinDDK\test1
【6】打开源文件:【File】——》【Open Source File】
【7】启动调试运行:使用命令 kd>g ,可以进入,也可以在菜单栏中【Debug】——》【Go】,还可以直接按F5
【8】切换到虚拟机,在控制台中键入 net start test1 命令启动服务
(注:必须是先启动Windbg的调试运行,然后在启动虚拟机中的test1服务才会在断点出停止。如果反过来的话,Windebg还没启动被调试机中的test1服务程序早就运行完了)
【9】在切换到调试机就可以看到Windbg已进入调试界面,并且在手工断点处停止,这时候可以在后面的代码中按F9加入断点然后进行调试。
下图为调试界面:
调试常用操作:
(1)在kd>后面输入g后按回车,能使中断的程序继续执行。
(2)选择菜单“Debug——》Break”使得当前被调试系统中断下来。这个是设置断点的前提条件。
(3)将光标移动到代码某一行上,然后按下F9键设置一个断点。请注意设置断点之前,系统必须已经中断;否在无法设置和修改断点。此外,再按一次F9键可以取消断点。
(4)按F10单步前进,也就是单步执行,遇到call指令不进入。
(5)按F11单步前进,但是遇到call指令进入。
注意:若出现虚拟机无法动弹的情况,不是因为死机了,而是windbg没有启动运行,导致虚拟机处于break out 的状态,这个时候一定要记得,在窗口的菜单栏中点击"GO",
或者在kd>后输入g,按回车,当状态处于busy状态的时候,就表示虚拟机开始运行了,这个时候就动起来啦!!