整个驱动程序非常简单,但它却是个完整意义上的驱动程序,包含了一个驱动程序所必须的各个部分。程序很简单,大家一看就明白,所以也没加什么注释,只捡几个重要的地方说明一下。
使用共享资源通常情况下需要考虑同步问题,即读写线程不能同时访问共享资源。在本例中总是只有一个线程,所以不需要同步。
代码: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,',',');把系统恢复成初始的样子。
关键字词: