北京2865信箱160分箱 冉林仓
1.引言:
在调试程序的时候,我们经常使用日志文件记录调试文件的运行结果,跟踪
程序运行的流程。通过这个文件,即便在调试过程中间系统崩溃,我们也能够从生成的日志文件中发现些可用信息。有的时候,这个日志文件的作用并不亚于一个调试器。
在调试用户态应用程序的时候,特别是那些与Com、外壳、钩子函数打交道的动态
链接库的时候,程序员习惯使用WritePrivateProfileString函数或者WritePrivateProfileInt函数,把程序运行的反馈信息记录到一个文本文件中;在调试wdm驱动程序的时候,编程人员也可以通过下列函数(ZwCreateFile、ZwWriteFile和ZwClose)找到替代方案。这些生成的日志文件,给程序的调试人员带来了极大的方便。然而,在编写VXD程序的时候,麻烦出现了,系统提供的VXD服务好像没有一个能够把日志信息输出到文件中,即便VToolsD提供了一些文件IO函数(比如:i_open、i_close、i_write等等),这些函数也只能在VXD初始化阶段才能被调用。一旦INIT_COMPLETE 消息被发送之后,这些函数将不能调用。文件系统钩子服务的确可以解决这个
问题,但是其烦琐的编程让驱动开发人员望而却步。这样,我们每次调试VXD程序的时候都不得不启动一个调试器来观察vxd输出结果,这对我们驱动
程序编程人员可以说是非常的不便。
其实Win 95和Win 98 系统都提供了一个名叫logger.vxd的文件,顾名思义,我们可以知道这个文件是生成日志记录文件用的。遗憾的是,在微软提供的DDK文档中,对这个VXD提供的服务只字不提,本文正是在借鉴文献(1)的基础上的对这个VXD的输出服务进行说明,以便任何一个VXD的编程人员都能使用logger.vxd记录
程序输出。
2.实现的原理:
A).在VXD中调用VXD
就像RING3 的DLL以一个标准的方式供EXE和DLL使用其输出函数一样,VXD中的函数也可以被其它的VXD按某种方式调用,当生成VXD时,所有可以被其它VXD调用的函数都被列在一个数组中,每个这样的函数也称作一个服务(SERVICE)。当一个VXD调用另一个VXD中的功能时,并不使用服务的名字,而是使用它在数组中的索引号,下面就是一个典型的声明:
Begin_Service_Table(LOGR0)
LOGR0_Service(LOGR0_Svc_GetVersion)
LOGR0_Service(LOGR0_Svc_PrintLog)
LOGR0_Service(LOGR0_Svc_CloseLog)
LOGR0_Service(LOGR0_Svc_OpenLog)
LOGR0_Service(LOGR0_Svc_FlushLog)
End_Service_Table
一个客户端VXD对服务器端VXD的LOGR0_Svc_GetVersion调用将调用0号服务, LOGR0_Svc_PrintLog服务调用将使用1号服务,其它依次类推。
为了存取服务端VXD的一个输出服务函数,客户端的VXD在调用VXDCall或VXDJmp宏的时候,必须传递一个服务端VXD的设备ID标识,和它的一个输出函数服务号。宏能够把它扩展成一个INT 20H中断,其后跟了一个双字,并根据设备ID和服务号查找目标函数的地址。当目标函数找到后,INT 20H和其后的双字就被CALL DWORD PTR [目标函数]所代替,它们恰好都是6个字节。有时这种机理也被称为RING0的动态链接。
B).VXD输出信息的获得
察看VXD输出服务需要Soft ICE 工具,通过输入VXD LOGGER 可以看到有关这个VXD的信息。在这些信息中,其中最主要的信息是,它是否为一个可动态加载的VXD
程序、它总共输出了几个VXD服务、它的设备ID号是什么。至于这些服务接口类型,就需要我们逆向工程进行反汇编来挖掘了,通过反汇编,我们至少从栈帧指针使用中了解参数的个数。接下来就要依靠我们平时积累的编程经验来推测参数类型了。譬如,按照惯例,VXD的第一个服务必须为GetVersion,通过反汇编,我们会发现logger.vxd也不例外。文件打开、读写、关闭函数一般情况下经常需要一些文件名、文件句柄等参数信息,事实上logger.