中间层
中科永联高级技术培训中心(www.itisedu.com)
中间层 (Middle Tier)也称作“应用程序服务器层或应用服务层”,是用户接口或 Web 客户端与数据库之间的逻辑层。典型情况下 Web 服务器位于该层,业务对象在此实例化。中间层是生成并操作接收信息的业务规则和函数的集合。它们通过业务规则(可以频繁更改)完成该任务,并由此被封装到在物理上与应用程序程序逻辑本身相独立的组件中。请参见客户端层、数据源层。
三层网络结构指的是将数据处理过程分为三部分:第一层是客户端(用户界面层),提供用户与系统的友好访问;第二层是应用服务层(也叫中间层),专司业务逻辑的实现;第三层是数据源层(数据服务层,数据库系统),负责数据信息的存储、访问及其优化。由于业务逻辑被提取到应用服务层,大大降低了客户端负担,因此也成为瘦客户(Thin Client)结构,三层结构在传统的二层结构的基础上增加了应用服务层,将应用逻辑单独进行处理,从而使得用户界面与应用逻辑位于不同的平台上,两者之间的通信协议由系统自行定义。通过这样的结构设计,使得应用逻辑被所有用户共享,这是两层结构应用软件与三层应用软件之间最大的区别。三层结构将表示部分和业务逻辑部分按照客户层和应用服务层相分离,客户端和应用服务层、应用服务层和数据库服务层之间的通讯、异构平台之间的数据交换等都可以通过中间件或者相关程序来实现。当数据库或者应用服务层的业务逻辑改变时,客户端并不需要改变,反之亦然,大大提高了系统模块的复用性,缩短开发周期,降低维护费用。以Java Applet为客户端, 以Java Servlet为中间层的三层网络结构,在目前的实时网络信息平台得到了广泛的应用,其结构和一般的三层结构如图1所示:
中间层驱动工作原理
(1)注册表常识:
1)、设备数据库所在的注册表健值为:
HKLM/SYSTEM/CurrentControlSet/Enum/
ENUM子项中是一个设备数据库,在数据库存放计算机中所有安装的,并且被系统认识到的设备。
所有的用户(包括管理员)都不能更改ENUM项的内容。这是为了保护操作系统和安装的设备的完整性。为了更改设备的设置,应该使用"设备管理器"。
为了在设备管理器中现实隐藏的,非即插即用的,以及没有连接到计算机上的所有设备,你应该首先在命令解释器中敲入命令set DEVMGR_SHOW_NONPRESENT_DEVICES=1,然后启动设备管理器,就可以在设备管理器中删除和重新配置这些设备了。
2)、硬件设备类所在的注册表健值为:
HKLM/SYSTEM/CurrentControlSet/Control/Class/
Class项下存放硬件设备类的配置信息。在Class项下的每个子项都代表一个设备类,子项的名称使用"唯一全局标识符(GUIS)",这些标识符存放该设备类的配置信息。在每个类标识符下,还会有以4位数命名的子项,他们代表该设备类里的具体设备,其他的配置数据只应用于该具体设备。
如网卡的设备类是{4D36E972-E325-11CE-BFC1-08002BE10318},并假定我们网卡对应的4为数命名子项名为0005。
其中
HKLM/SYSTEM/CurrentControlSet/Class/{4D36E972-E325-11CE-BFC1-08002BE10318}/0005/Linkage/ 中:
Export:代表该设备在设备名字空间输出的设备名字。
RootDevice:代表当前设备的GUID。中间层驱动这里有两个GUID,第一个是自己的GUID,第二个是该中间层驱动绑定的下层MINIPORT的GUID。
UpperBind:代表上层绑定它的NDIS协议驱动或NDIS中间层驱动。当某个协议驱动绑定该MINIPORT设备时,则这个协议驱动的名字必须出现在UpperBind健值的字符串中,否则不能进行绑定。也就是说,UpperBind健值的字符串决定了那个协议驱动(当然也包括中间层驱动注册的协议)和当前的MINIPORT设备绑定,即,它决定了NDIS的上下层绑定关系。
注:一般添加中间层驱动后,中间层驱动只插入到真是网卡和相应协议中间,不会插入到虚拟网卡(如安装虚拟机后虚拟出来的网卡设备)和相应协议中间。
3)、驱动程序所在的注册表健值为:
HKLM/SYSTEM/CurrentControlSet/Services
ENUM子项
通常如果某个服务下存在ENUM子项,表明该服务是用来控制某个设备或者设备交互的,它的下面存放该设备的实例。用户不要去试图修改该子项的内容,因为每次系统启动时,都会重写该子项的内容。
LINKAGE子项
值项Bind
存放该协议所在绑定栈的最低层小端口设备实例(即MINIPORT)。
值项Export
存放该服务必须访问的对象,该对象必须已经安装在系统中,并且该服务能够使用。
值项Route
指定子项Linkage从那里获取绑定数据。
Parameters/ Adapters/子项
这里,我们只解释中间层驱动中该子项的意义。为此,我们假设当前我们讨论的HKLM/SYSTEM/CurrentControlSet/Services/XXXX中的XXXX为中间层驱动。
对于中间层驱动,该子项下含有一个子项,是以我们当前中间层驱动绑定的下层MINIPORT设备的GUID命名的,在我的系统中,健值如下:
{102454C2-9DB3-42A1-B4CF-6A8B67A516C0}
在{102454C2-9DB3-42A1-B4CF-6A8B67A516C0}子项有个健,名称为UpperBindings,该健的健值是当前中间层驱动的MINIPORT设备名称,在我的系统中为如下健值:
/Device/ {5BF5A311-13E4-4746-8865-339DDD6C73AF}
(2)中间层驱动
在我们的函数NDIS_PROTOCOL_CHARACTERISTICS-> BindAdapterHandler()中,会调用一系列函数(如NdisOpenProtocolConfiguration、NdisReadConfiguration)来访问注册表,其实都是访问Parameters/Adapters/{102454C2-9DB3-42A1-B4CF-6A8B67A516C0}子项。
注意,函数NDIS_PROTOCOL_CHARACTERISTICS-> BindAdapterHandler()的倒数第二个参数是SystemSpecific1,如果我们安装的是XPASSTHRU,则其具体指的是如下字符串:
xfilter/Parameters/Adapters/{102454C2-9DB3-42A1-B4CF-6A8B67A516C0}
其中xfilter 是XPASSTHRU的,而{102454C2-9DB3-42A1-B4CF-6A8B67A516C0}在不同的系统中不同。
函数NdisOpenProtocolConfiguration()其实只是构造一个查询注册表的RTL_QUERY
_REGISTRY_TABLE结构(该结构在利用函数RtlQueryRegistryValues()查询注册表是使用)。并将这个结构封装到NDIS_WRAPPER_CONFIGURATION_HANDLE结构中,然后作为NdisOpenProtocolConfiguration()的第二个参数返回。
其实NdisOpenProtocolConfiguration()构造的RTL_QUERY_REGISTRY_TABLE结构的含义也就是查询HKLM/SYSTEM/CurrentControlSet/Services/ xfilter/Parameters/Adapters/{
102454C2-9DB3-42A1-B4CF-6A8B67A516C0}/下的健值(xfilter 会随着安装不同的中间层驱动而不同,{102454C2-9DB3-42A1-B4CF-6A8B67A516C0}在不同的系统中不同,下面均省略这些注释)。
函数NdisReadConfiguration()有两个作用,它首先修改在函数NdisOpenProtocolConf
iguration()构造的RTL_QUERY_REGISTRY_TABLE结构。也就是在查询HKLM/SYSTEM
/CurrentControlSet/Services/ xfilter/Parameters/Adapters/{102454C2-9DB3-42A1-B4CF-6A8B6
7A516C0}/的基础上加上了一个健,将其变成HKLM/SYSTEM/CurrentControlSet/Services/ xfilter/Parameters/Adapters/{102454C2-9DB3-42A1-B4CF-6A8B67A516C0}/UpperBindings。然后函数NdisReadConfiguration()会调用函数RtlQueryRegistryValues()查询新构造的这个注册表,并将结果存储在调用函数NdisReadConfiguration()时的第二个参数中。在我的系统中,RtlQueryRegistryValues()读出的这个新构造的这个注册表的健值是:/Device/ {5BF5A311-13E4-4746-8865-339DDD6C73AF}(后面我们称为RESULT1),它其实是我们注册的中间层驱动(XPASSTHRU)的设备输出(其构造是/Device+/GUID)。
其实我们读出的这个中间层驱动(XPASSTHRU)的设备(即我们刚才读出的那个健值RESULT1)只在后面的函数NdisIMInitializeDeviceInstanceEx()中才用得着,并且RESULT1是作为函数NdisIMInitializeDeviceInstanceEx()的第二个参数的。
这里有个地方让人感到迷惑,即在函数BindAdapterHandler()我们一旦调用了函数NdisOpenAdapter()绑定了一个下层的MINIPORT,为什么还要调用函数NdisIMInitializeDeviceInstanceEx()初始化我们中间层驱动自己的MINIPORT(因为函数NdisIMInitializeDeviceInstanceEx()的参数是RESULT1,而RESULT1代表我们中间层驱动的MINIPORT)。为了解释这个原因,我们先做如下假设。
我们假设我们系统安装了一个中间层驱动(假设为XPASSTRHU),在上图中,PROT-IM和MINIPORT-IM分别代表我们中间层驱动的协议驱动程序和小端口驱动程序,PROT-TCPIP代表真正的协议驱动程序,MINIPORT-NIC代表真是网卡的小端口驱动程序。
当PROT-TCPIP需要发送数据时,会调用函数NdisSend(),其实它会调用MINIPORT-IM中的发送数据函数,但是MINIPORT-IM和MINIPORT-NIC没联系,按照常规是不能发送数据到MINIPORT-NIC的。但是我们可以看到PROT-IM是可以和MINIPORT-NIC互相发送数据的,所以我们必须将MINIPORT-IM和PROT-IM联系起来。
另外当MINIPORT-NIC需要将数据提交给PROT-TCPIP时,只能首先将数据提交给PROT-IM,PROT-IM也只能通过MINIPORT-IM才能和PROT-TCPIP联系起来。
所以必须将PROT-IM和MINIPORT-IM联系起来。有人说这两个东西都是我们中间层驱动注册得,难道还联系不起来吗?不错,但是不管怎样你都得通过你得代码才能将他们联系起来呀!下面我们就介绍联系得方法。
我们以XPASSTHRU为例,在其函数ProtocolBindAdapter()中,先分配了一个ADAPT结构(这个结构是自己定义的,可根据用户的需要定义)。当调用函数ProtocolBindAdapter()调用函数NdisOpenAdapter()进行绑定时,是以&Adapt->BindingHandle作为函数NdisOpenAdapter()的第三个参数的,这样Adapt->BindingHandle就指向了NDIS_OPEN_BLOCK1(注意,调用函数NdisOpenAdapter()时的第三个参数会返回指向绑定以后的NDIS_OPEN_BLOCK指针)。然后函数ProtocolBindAdapter()会调用函数NdisIMInitializeDeviceInstanceEx(),该函数会进一步调用XPASSTHRU的NDIS_MINIPORT_CHARACTERISTICS->InitializeHandler()函数。上面我们讨论过,NdisIMInitializeDeviceInstanceEx()中的第二个参数就是XPASSTHRU注册的小端口驱动的设备实例(即上面的RESULT1)。在函数NdisIMInitializeDeviceInstanceEx()中会根据设备名称(RESULT1),找到对应的MINIPORT结构,并将其指针作为参数传递给NDIS_MINIPORT_CHARACTERISTICS->InitializeHandler()函数(以后简称InitializeHandler()函数)。在InitializeHandler()函数中,会进一步将MINIPORT结构指针赋值给ADAPT->MiniportHandle(ADAPT就是上面在函数ProtocolBindAdapter()中分配的那个结构)。
这样数据结构ADAPT中就含有了两个指针,一个指向NDIS_OPEN_BLOCK1,另一个指向MINIPORT-IM,而NDIS_OPEN_BLOCK1和PROT-IM是密切联系的,所以ADAPT就将PROT-IM和MINIPORT-IM紧密的联系起来了。
另外注意,NDIS_MINIPORT_BLOCK->DeviceContext是指向我们的ADAPT结构的,这个赋值在函数NdisIMInitializeDeviceInstanceEx()中完成。上面谈到了也是在函数NdisIMInitializeDeviceInstanceEx()中完成了从ADAPT-> MiniportHandle到MINIPORT的绑定,所以在函数NdisIMInitializeDeviceInstanceEx()中完成了ADAPT和MINIPORT的互相连接(即指针互相指向)。
函数InitializeHandler()中可以利用句柄MINIPORT的句柄得到我们的ADAPT结构,具体实现这个功能的函数是NdisIMGetDeviceContext(),这个函数的参数是个NDIS_HANDLE类型,但是在该函数内部,会将这个参数转换成NDIS_MINIPORT_BLOCK结构,并返回NDIS_MINIPORT_BLOCK->DeviceContext。
在函数NdisOpenAdapter()中,除了上面提到的完成了由Adapt->BindingHandle到NDIS_OPEN_BLOCK1的指向外,还完成了我们没有提到的由NDIS_OPEN_BLOCK1-> ProtocolBindingContext到ADAPT的指向,所以函数NdisOpenAdapter()完成了ADAPT和NDIS_OPEN_BLOCK1的互相连接(即指针互相指向)。
讲到这里,我们上图完善为下图:
下面大家也许就可以做一些HOOK了,例如对OPEN_BLOCK结构的HOOK,对ADAPT的HOOK等,这里就不作介绍了(作者:noble 出处:http://www.nsfocus.com)