Delphi驱动开发研究第六篇--与用户进程通讯(section篇)

  在进入主题之前,先来简单地看一下结构化异常处理(Structured Exception Handling, SEH),本篇的程序需要这个东东。
    结构化异常处理
   这里我并不打算详细讲结构化异常处理,关于SEH,在网上你能找到相关的内容,SHE能用于所有的异常处理,也就是说,SEH既能用于用户模式又能用于内核模式。但这两种模式下的异常处理有一个本质上的差别:
   在内核模式下,借助于seh,并非所有的异常都能得到处理!比如说,即使使用了seh,用零作除数作除法也会使系统崩溃。最为可怕的是,引用未定义的内核内存也会导致蓝屏死机BSOD。而对未定义的用户模式内存的引用异常,seh却可以轻松处理。因此避免系统崩溃的唯一办法就是所编写的代码不要导致无法处理的异常。
以下是个使用结构化异常的例子:
unit seh;

interface

uses
   nt_status;

function _DriverEntry(pDriverObject:PDRIVER_OBJECT;
                       pusRegistryPath:PUNICODE_STRING): NTSTATUS; stdcall;   

implementation

uses
   ntoskrnl;

const
   SEH_SafePlaceCounter   = 0;
   SEH_INSTALLED           = 0;

type
   _SEH = record
     SafeEip: DWORD; { 线程继续执行的地方 }
     PrevEsp: DWORD; { 以前esp的值 }
     PrevEbp: DWORD; { 以前ebp的值 }
   end;

var
   sseh: _SEH;

function DefaultExceptionHandler(pExcept:PEXCEPTION_RECORD;
                                  pFrame:DWORD;
                                  pContext:PCONTEXT;
                                  pDispatch:DWORD): DWORD; cdecl;
begin
   DbgPrint(#13#10'SEH: An exception %08X has occured'#13#10,
            pExcept^.ExceptionCode);
   if pExcept^.ExceptionCode = $0C0000005 then
   begin
     {如果发生了EXCEPTION_ACCESS_VIOLATION类型的异常,}
     {则输出以下信息.}
     DbgPrint(' Access violation at address: %08X'#13#10,
              pExcept^.ExceptionAddress);
     if pExcept^.ExceptionInformation[0] <> nil then   {试图读还是写?}
     begin
       DbgPrint(' The code tried to write to address %08X'#13#10#13#10,
                DWORD(pExcept^.ExceptionInformation[4]));
     end else
     begin
       DbgPrint(' The code tried to read from address %08X'#13#10#13#10,
                DWORD(pExcept^.ExceptionInformation[4]));
     end;
   end;
   asm
     lea eax, sseh
     push (_SEH PTR [eax]).SafeEip
     push (_SEH PTR [eax]).PrevEsp
     push (_SEH PTR [eax]).PrevEbp

     mov eax, pContext
     pop (CONTEXT PTR [eax]).regEbp
     pop (CONTEXT PTR [eax]).regEsp
     pop (CONTEXT PTR [eax]).regEip
   end;
   result := 0;
end;

procedure BuggyReader; assembler;
asm
   xor eax, eax
   mov eax, [eax]   {!!! 没有SEH的话 - BSOD !!!}
end;

procedure BuggyWriter; assembler;
asm
   mov eax, offset MmUserProbeAddress
   mov eax, [eax]
   mov eax, [eax]

   mov byte ptr [eax], 0 {!!!没有SEH的话 - BSOD !!!}
end;

function _DriverEntry(pDriverObject:PDRIVER_OBJECT;
                       pusRegistryPath:PUNICODE_STRING): NTSTATUS; stdcall;
label
   SafePlace;
begin
   DbgPrint(#13#10'SEH: Entering DriverEntry'#13#10);
   { "手工"安装SEH }
   asm
     push offset DefaultExceptionHandler   {我们的SEH程序}
     push fs:[0]
     mov fs:[0], esp

     mov sseh.SafeEip, offset SafePlace   {SafePlace是处理完异常后继续执行的地方}
     mov sseh.PrevEbp, ebp
     mov sseh.PrevEsp, esp
   end;
   BuggyReader;
   BuggyWriter;

SafePlace:
   asm
     pop fs:[0]
     add esp, 4
   end;
   
   DbgPrint(#13#10'SEH: Leaving DriverEntry'#10#13);
   result := STATUS_DEVICE_CONFIGURATION_ERROR;
end;

end.


    安装SHE-Frame
   由于在内核模式下我们无法直接使用Delphi自身的异常处理机制,因为在驱动程序中我们要自己手工安装SHE,这里我们使用Delphi的BASM来做这件事情,了解SHE的朋友都知道,做这件事情是非常简单的。
asm
     push offset DefaultExceptionHandler   {我们的SEH程序}
     push fs:[0]
     mov fs:[0], esp

     mov sseh.SafeEip, offset SafePlace   {SafePlace是处理完异常后继续执行的地方}
     mov sseh.PrevEbp, ebp
     mov sseh.PrevEsp, esp
end;

  
   为了在异常处理之后我们的处理程序能恢复线程的执行,我们应该保存esp、ebp寄存器的内容以及线程继续执行的地址。我们将这三项信息保存在seh结构体中并调用函数BuggyReader。BuggyReader函数试图从地址00000000读取一个DWORD。

procedure BuggyReader; assembler;
asm
   xor eax, eax
   mov eax, [eax]   {!!! 没有SEH的话 - BSOD !!!}
end;

    nil指针引用是一个十分常见的错误,微软在00000000-0000FFFFh划出了64k字节的内存区,并使此区域无法访问。访问此区域中的任何一个字节都会导致EXCEPTION_ACCESS_VIOLATION类型的异常。
    异常处理
   函数BuggyReader从地址00000000读取引发了异常,我们就进入了我们指定的处理程序。
function DefaultExceptionHandler(pExcept:PEXCEPTION_RECORD;
                                  pFrame:DWORD;
                                  pContext:PCONTEXT;
                                  pDispatch:DWORD): DWORD; cdecl;
begin
DbgPrint(#13#10'SEH: An exception %08X has occured'#13#10,
            pExcept^.ExceptionCode);
   if pExcept^.ExceptionCode = $0C0000005 then
   begin
     {如果发生了EXCEPTION_ACCESS_VIOLATION类型的异常,}
     {则输出以下信息.}
     DbgPrint(' Access violation at address: %08X'#13#10,
              pExcept^.ExceptionAddress);
     if pExcept^.ExceptionInformation[0] <> nil then   {试图读还是写?}
     begin
       DbgPrint(' The code tried to write to address %08X'#13#10#13#10,
                DWORD(pExcept^.ExceptionInformation[4]));
     end else
     begin
       DbgPrint(' The code tried to read from address %08X'#13#10#13#10,
                DWORD(pExcept^.ExceptionInformation[4]));
     end;
   end;
   asm
     lea eax, sseh
     push (_SEH PTR [eax]).SafeEip
     push (_SEH PTR [eax]).PrevEsp
     push (_SEH PTR [eax]).PrevEbp

     mov eax, pContext
     pop (CONTEXT PTR [eax]).regEbp
     pop (CONTEXT PTR [eax]).regEsp
     pop (CONTEXT PTR [eax]).regEip
   end;
   result := 0;
end;
  
    我们处理的第一件事就是输出相应的调试信息,如果发生的是EXCEPTION_ACCESS_VIOLATION类型的异常,还要输出一些额外的信息。之后开始真正的异常处理(这里了使用了BASM,你可以从中体会到BASM的强大功能)。
asm
     lea eax, sseh
     push (_SEH PTR [eax]).SafeEip
     push (_SEH PTR [eax]).PrevEsp
     push (_SEH PTR [eax]).PrevEbp

     mov eax, pContext
     pop (CONTEXT PTR [eax]).regEbp
     pop (CONTEXT PTR [eax]).regEsp
     pop (CONTEXT PTR [eax]).regEip
end;

    在本例中异常处理只是简单地恢复esp、ebp寄存器的值并将eip寄存器的值置为一个能使线程安全地继续执行的地址。这些处理信息都是由系统从我们预先填充的seh结构体中取出并保存在了CONTEXT结构体中,CONTEXT结构体的指针又传递给了系统。
   最后异常处理函数返回ExceptionContinueExecution,该值为零就是告诉系统应该恢复线程的上下文并继续执行。即eip的值等于标记SafePlace的地址,而esp和ebp寄存器的值恢复为原值,线程从标记SafePlace处继续其执行。还有一点要注意,异常处理程序是C调用约定的。
   有了结构化异常处理的知识后,让我们继续我们的内核之旅。接下来的例子中我们要从驱动程序中转到用户模式内存里。这个转换最好能包含在SEH-frame里。
    内存共享
   Windows提供了许多机制来进行进程间通讯(Interprocess Communications, IPC):通讯缓冲、DDE、通讯窗口(WM_COPYDATA就在这里)、邮槽(mailslot)、sockets等等。所有这些机制都是基于文件映射对象(file-mapping object)的,该对象本身是一块两个或多个进程可以访问的内存区,用DDK的术语,映射文件就是section对象,不要把它和PE文件中的section混淆起来。
   section对象是最底层的通讯机制,这种对象被系统用来将可执行映象加载到内存,而缓存调度程序用它来访问缓存文件中的数据。section对象还能将磁盘上的文件映射到进程的地址空间中,而且用起来不像是在用文件,而是在用内存块。
   借助于section对象来共享数据的情形如下:一个进程调用函数CreateFileMapping创建了一个内存映射文件。之后调用函数MapViewOfFile(如果层次更低就调用NtMapViewOfSection)将其视图(view)映射到自己的地址空间中,而另一个进程通过OpenFileMapping打开这个映射文件,并将其映射到自己的地址空间中。结果同一组物理内存页变为由两个进程访问,这就使得它们能通过这个区域轻松地传递较大量的数据,一个进程对这些页内容的修改会反映到另一个进程中。
   共享section这种通讯方法不止可以用在用户进程间,还可以用在驱动程序里。在下面的例子里我们用命名section来在用户进程和驱动程序之间进行通讯。
   老规矩,先来看看驱动程序。
unit SharedSection;

interface

uses
   nt_status, ntoskrnl, native, winioctl, fcall, macros;

function _DriverEntry(pDriverObject:PDRIVER_OBJECT; pusRegistryPath:PUNICODE_STRING): NTSTATUS; stdcall;

implementation

uses
   seh;

const
   SECTION_SIZE            = $1000;

var
   g_usDeviceName, g_usSymbolicLinkName, g_usSectionName: UNICODE_STRING;

function DispatchCreateClose(p_DeviceObject:PDEVICE_OBJECT; p_Irp:PIRP): NTSTATUS; stdcall;
begin
   p_Irp^.IoStatus.Status := STATUS_SUCCESS;
   p_Irp^.IoStatus.Information := 0;

   IofCompleteRequest(p_Irp, IO_NO_INCREMENT);
   result := STATUS_SUCCESS;
end;

function DispatchControl(p_DeviceObject: PDEVICE_OBJECT; p_Irp:PIRP): NTSTATUS; stdcall;
label
   SafePlace;
var
   status:NTSTATUS;
   IOCTL_SHARE_MY_SECTION: DWORD;
   psl:PIO_STACK_LOCATION;
   oa:OBJECT_ATTRIBUTES;
   hSection:HANDLE;
   pSectionBaseAddress:PVOID;
   liViewSize:LARGE_INTEGER;
begin
   IOCTL_SHARE_MY_SECTION := CTL_CODE(FILE_DEVICE_UNKNOWN, $800, 0, 0);
   psl := IoGetCurrentIrpStackLocation(p_Irp); {取IRP的stack location的指针}
   if psl^.Parameters.DeviceIoControl.IoControlCode = IOCTL_SHARE_MY_SECTION then
   begin
     {是我们控制码就开始处理}
     DbgPrint('SharedSection: Opening section object'#10#13);
     RtlInitUnicodeString(g_usSectionName, '\BaseNamedObjects\UserKernelSharedSection');
     InitializeObjectAttributes(oa, @g_usSectionName,
                                OBJ_CASE_INSENSITIVE, 0, nil);
     status := ZwOpenSection(@hSection,
                             SECTION_MAP_WRITE or SECTION_MAP_READ,
                             @oa);
     if status = STATUS_SUCCESS then
     begin
       DbgPrint('SharedSection: Section object opened'#13#10);
       pSectionBaseAddress := nil;
       liViewSize.HighPart := 0;
       liViewSize.LowPart := 0;
       status := ZwMapViewOfSection(hSection, HANDLE(NtCurrentProcess),
                                    pSectionBaseAddress, 0,
                                    SECTION_SIZE, nil,
                                    @liViewSize, ViewShare, 0,
                                    PAGE_READWRITE);
       if status = STATUS_SUCCESS then
       begin
         DbgPrint('SharedSection: Section mapped at address %08X'#13#10,
                  pSectionBaseAddress);
         {安装SEH}
         asm
           push offset DefaultExceptionHandler
           push fs:[0]
           mov fs:[0], esp

           mov sseh.SafeEip, offset SafePlace
           mov sseh.PrevEbp, ebp
           mov sseh.PrevEsp, esp
         end;
         _strrev(pSectionBaseAddress);
         p_Irp^.IoStatus.Status := STATUS_SUCCESS;
         DbgPrint('SharedSection: String reversed'#13#10);
SafePlace:
         asm
           pop fs:[0]
           add esp, 4
         end;
         ZwUnmapViewOfSection(HANDLE(NtCurrentProcess), pSectionBaseAddress);
         DbgPrint('SharedSection: Section at address %08X unmapped '#13#10,
                  pSectionBaseAddress);
       end else
       begin
         DbgPrint('SharedSection: Couldn''t map view of section. Status: %08X'#13#10,
                  status);
       end;
       ZwClose(hSection);
       DbgPrint('SharedSection: Section object handle closed'#13#10);
     end else
     begin
       DbgPrint('SharedSection: Couldn''t open section. Status: %08X'#13#10, status);
     end;
   end else
   begin
     status := STATUS_INVALID_DEVICE_REQUEST;
   end;
   p_Irp^.IoStatus.Status := status;   
   IofCompleteRequest(p_Irp, IO_NO_INCREMENT);
   DbgPrint('SharedSection: Leaving DispatchControl'#13#10);
   result := status;
end;

{卸载驱动}
procedure DriverUnload(p_DriverObject:PDRIVER_OBJECT); stdcall;
begin
   IoDeleteSymbolicLink(@g_usSymbolicLinkName);
   IoDeleteDevice(p_DriverObject^.DeviceObject);
end;   

{驱动进入点}
function _DriverEntry(pDriverObject:PDRIVER_OBJECT; pusRegistryPath:PUNICODE_STRING): NTSTATUS;
var
   status: NTSTATUS;
   pDeviceObject: TDeviceObject;
begin
   status := STATUS_DEVICE_CONFIGURATION_ERROR;
   RtlInitUnicodeString(@g_usDeviceName, '\Device\SharedSection');
   RtlInitUnicodeString(@g_usSymbolicLinkName, '\DosDevices\SharedSection');

   if IoCreateDevice(pDriverObject, 0, @g_usDeviceName,
                     FILE_DEVICE_UNKNOWN, 0, TRUE,
                     pDeviceObject) = STATUS_SUCCESS then
   begin
     if IoCreateSymbolicLink(@g_usSymbolicLinkName,
                             @g_usDeviceName) = STATUS_SUCCESS then
     begin
       pDriverObject^.MajorFunction[IRP_MJ_CREATE] := @DispatchCreateClose;
       pDriverObject^.MajorFunction[IRP_MJ_CLOSE] := @DispatchCreateClose;
       pDriverObject^.MajorFunction[IRP_MJ_DEVICE_CONTROL] := @DispatchControl;
       pDriverObject^.DriverUnload := @DriverUnload;
       status := STATUS_SUCCESS;
     end else
     begin
       IoDeleteDevice(@pDeviceObject);
     end;
   end;
   result := status;
end;

end.

    整个驱动程序非常简单,但它却是个完整意义上的驱动程序,包含了一个驱动程序所必须的各个部分。程序很简单,大家一看就明白,所以也没加什么注释,只捡几个重要的地方说明一下。
   使用共享资源通常情况下需要考虑同步问题,即读写线程不能同时访问共享资源。在本例中总是只有一个线程,所以不需要同步。 
InitializeObjectAttributes(oa, @g_usSectionName, OBJ_CASE_INSENSITIVE, 0, nil);

    取得控制代码 IOCTL_SHARE_MY_SECTION,驱动程序尝试打开section对象,其名字定义为变量g_usSectionName。 
status := ZwOpenSection(@hSection,
                       SECTION_MAP_WRITE or SECTION_MAP_READ,
                       @oa);
if status = STATUS_SUCCESS then
begin
    DbgPrint('SharedSection: Section object opened'#13#10);
    pSectionBaseAddress := nil;
    liViewSize.HighPart := 0;
    liViewSize.LowPart := 0;
    status := ZwMapViewOfSection(hSection, HANDLE(NtCurrentProcess),
                              pSectionBaseAddress, 0,
                              SECTION_SIZE, nil,
                              @liViewSize, ViewShare, 0,
                              PAGE_READWRITE);

     如果取得了section的句柄,我们就来映射它的视图。这里与应用程序中的映射视图部分几乎完全相同。但是在调用了ZwMapViewOfSection之后,变量pSectionBaseAddress保存的值位于用户地址区域中,而不是核心区域中的地址,这点我们可以预料到。这可是个原则上的问题,即对这个地址的使用将只能在这个进程的上下文中,在映射section的地址空间中。由于SharedSection驱动程序是单层的(您还记得IRP_MJ_DEVICE_CONTROL类型的IRP处理要经过驱动程序进入发出该操作的线程上下文中),所以我们位于我们的应用程序的上下文中。
    这里视图的虚拟地址与应用程序中视图的地址将有所不同,但共享section所在的物理页是同一个。我们这里有一个内存页,其中还保存着倒着写的一行文字。
asm
    push offset DefaultExceptionHandler
    push fs:[0]
    mov fs:[0], esp

    mov sseh.SafeEip, offset SafePlace
    mov sseh.PrevEbp, ebp
    mov sseh.PrevEsp, esp
end;
_strrev(pSectionBaseAddress);
p_Irp^.IoStatus.Status := STATUS_SUCCESS;
DbgPrint('SharedSection: String reversed'#13#10);
SafePlace:
asm
    pop fs:[0]
    add esp, 4
end;
  
    以上建立SEH-frame,并调用_strrev函数将内存里的字符串反转过来。
   下面来看看在用户模式下如何加载和调用这个驱动程序。
program SharedSection;

{$APPTYPE CONSOLE}

uses
    SysUtils,
    Windows,
    Dialogs,
    WinSvc,
    nt_status,
    native,
    macros,
    ntdll;

const
    SECTION_SIZE = $1000;
    str = '.revird ecived a dna sessecorp resu neewteb yromem erahs ot euqinhcet emas eht esu nac uoy ,revewoH .sessecorp resu gnoma yromem gnirahs rof desu euqinhcet nommoc a si elif gnigap eht yb dekcab elif deppam-yromem A';
    _DELETE = $10000;

var
    hSection:HANDLE;
    liSectionSize: LARGE_INTEGER;
    oa:OBJECT_ATTRIBUTES;
    pSectionBaseAddress:PVOID;
    liViewSize: LARGE_INTEGER;
    g_usSectionName: UNICODE_STRING;
    status:NTSTATUS;
    sTemp: array[0..255] of char;

function CallDriver: boolean;
var
    fOk: boolean;
    hSCManager:HANDLE;
    hService:HANDLE;
    acModulePath: string;
    _ss:SERVICE_STATUS;
    hDevice:HANDLE;

    dwBytesReturned: DWORD;
    IOCTL_SHARE_MY_SECTION: DWORD;
    lpTemp: PChar;
begin
    fOk := false;
    IOCTL_SHARE_MY_SECTION := CTL_CODE(FILE_DEVICE_UNKNOWN, $800, 0, 0);
    hSCManager := OpenSCManager(nil, nil, SC_MANAGER_ALL_ACCESS);
    if hSCManager <> 0 then
    begin
      acModulePath := GetCurrentDir + '\' + ExtractFileName('SharedSection.sys');
      hService := CreateService(hSCManager, 'SharedSection',
                                'One way to share section',
                                SERVICE_START or SERVICE_STOP or _DELETE,
                                SERVICE_KERNEL_DRIVER,
                                SERVICE_DEMAND_START,
                                SERVICE_ERROR_IGNORE,
                                PChar(acModulePath),
                                nil, nil, nil, nil, nil);

      if hService <> 0 then
      begin
        if StartService(hService, 0, lpTemp) then
        begin;
          hDevice := CreateFile(PChar('\\.\SharedSection'), 0, 0,
                                nil, OPEN_EXISTING, 0, 0);
          if hDevice <> INVALID_HANDLE_VALUE then
          begin
            if DeviceIoControl(hDevice, IOCTL_SHARE_MY_SECTION,
                               nil, 0, nil, 0, dwBytesReturned,
                               nil) then
            begin
              fOk := true;
            end else
            begin
              ShowMessage('Can''t send control code to device.');
            end;
            CloseHandle(hDevice);
          end else
          begin
            ShowMessage('Device is not present.');
          end;
          ControlService(hService, SERVICE_CONTROL_STOP, _ss);
        end else
        begin
          ShowMessage('Can''t start driver.');
        end;
        DeleteService(hService);
        CloseServiceHandle(hService);
      end else
      begin
        ShowMessage('Can''t register driver.');
      end;
      CloseServiceHandle(hSCManager);
    end else
    begin
      ShowMessage('Can''t connect to Service Control Manager.');
    end;
    result := fOk;
end;

begin
    liSectionSize.HighPart := 0;
    liSectionSize.LowPart := SECTION_SIZE;
    RtlInitUnicodeString(g_usSectionName, '\BaseNamedObjects\UserKernelSharedSection');
    InitializeObjectAttributes(oa, @g_usSectionName,
                               OBJ_CASE_INSENSITIVE, 0, nil);
    status := ZwCreateSection(@hSection, SECTION_MAP_WRITE or SECTION_MAP_READ,
                              @oa, @liSectionSize, PAGE_READWRITE,
                              SEC_COMMIT, 0);
    if status = STATUS_SUCCESS then
    begin
      pSectionBaseAddress := nil;
      liViewSize.HighPart := 0;
      liViewSize.LowPart := 0;
      status := ZwMapViewOfSection(hSection, HANDLE(NtCurrentProcess),
                                   pSectionBaseAddress, 0,
                                   SECTION_SIZE, nil, @liViewSize,
                                   ViewShare, 0, PAGE_READWRITE);
      if status = STATUS_SUCCESS then
      begin
        //RtlInitUnicodeString(g_szStrToReverse, str);
        strcpy(pSectionBaseAddress, PChar(str));
        if CallDriver then
        begin
          strcpy(sTemp, pSectionBaseAddress);
          ShowMessage(sTemp);
          ZwUnmapViewOfSection(HANDLE(NtCurrentProcess),
                               pSectionBaseAddress);
        end;
      end else
      begin
        ShowMessage('Can''t map section.');
      end;
      ZwClose(hSection);
    end else
    begin
      ShowMessage('Can''t create section.');
    end;

end.
  
    这是个控制台应用程序,我们也只讲几个关键的地方,其他地方很容易理解。
liSectionSize.HighPart := 0;
liSectionSize.LowPart := SECTION_SIZE;

    建立section需要指明其大小,对于大小值使用LARGE_INTEGER类型的变量的程序来说,这个值可以超过4GB。我们将这个值初始化为一个内存页的大小即4KB。
InitializeObjectAttributes(oa, @g_usSectionName,
                       OBJ_CASE_INSENSITIVE, 0, nil);

    对于宏InitializeObjectAttributes我们已经很熟悉了。后面的ZwCreateSection调用需要填充好的OBJECT_ATTRIBUTES结构体,填充工作我们用这些宏来完成。
   我们准备使用的section应该取个名字,这样就可以用名字来打开它。section的名字必须就unicode字符串,通过RtlInitUnicodeString(g_usSectionName, '\BaseNamedObjects\UserKernelSharedSection');来创建。
section对象在对象管理器名字空间的BaseNamedObjects目录下,用户进程创建的命名对象一般都在这个目录下。
status := ZwCreateSection(@hSection, SECTION_MAP_WRITE or SECTION_MAP_READ,
                        @oa, @liSectionSize, PAGE_READWRITE,
                        SEC_COMMIT, 0);

    调用函数ZwCreateSection来创建命名section对象,其大小为SECTION_SIZE,访问方式为可读写。如果section创建成功,则可以从变量hSection中得到其句柄。
if status = STATUS_SUCCESS then
begin
    pSectionBaseAddress := nil;
    liViewSize.HighPart := 0;
    liViewSize.LowPart := 0;
    status := ZwMapViewOfSection(hSection, HANDLE(NtCurrentProcess),
                               pSectionBaseAddress, 0,
                               SECTION_SIZE, nil, @liViewSize,
                               ViewShare, 0, PAGE_READWRITE);

    这样就将section的所有的视图都映射到内存中。这个函数的参数够多的——所有的参数在DDK中都有详细介绍。参数pSectionBaseAddress初始化为NULL,为更方便她映射section,系统自己定义了映射section的虚拟地址,并将这个地址返给此参数。参数liViewSize初始化为零,这就定义了section将被全部映射。
.revird ecived a dna sessecorp resu neewteb yromem erahs ot euqinhcet emas eht esu nac uoy ,revewoH .sessecorp resu gnoma yromem gnirahs rof desu euqinhcet nommoc a si elif gnigap eht yb dekcab elif deppam-yromem A

    我们将一行倒着写的文字拷贝到所得的视图中。驱动程序的任务就是将这行文字翻转,使其变为可读的形式。
if status = STATUS_SUCCESS then
begin
    strcpy(pSectionBaseAddress, PChar(str));
    if CallDriver then
    begin
      strcpy(sTemp, pSectionBaseAddress);
      ShowMessage(sTemp);
      ZwUnmapViewOfSection(HANDLE(NtCurrentProcess),
                       pSectionBaseAddress);
    end;

    CallDriver的返回值为TRUE表示驱动程序已成功完成自己的任务。我们来检查一下其工作的结果。在函数CallDriver中,我们完成通常的注册和启动驱动程序的操作并向驱动程序发送控制代码IOCTL_SHARE_MY_SECTION。
   最后ZwUnmapViewOfSection(HANDLE(NtCurrentProcess),                      pSectionBaseAddress);把系统恢复成初始的样子。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值