驱动笔记之一:内核命名管道问题的解决

(老鸟们请不要笑话我……)

最近正在做一个驱动程序,需要用到内核与用户层通信。我是个驱动开发的新手,所以想回归最简单最传统的 Windows 编程模式,第一个反应就是:命名管道。所以不要问我为什么不用 minifilter 的通信机制,原因很简单——我不会。

内核管道的实现在这里:http://blog.csdn.net/skyseacsn/article/details/6962858

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
#pragma pack(push,1)
typedef struct _WAIT_PIPE_PARAM
{
__int64 liTimeOutvalue;
ULONG ulPipeNameLen;
USHORT bUsTimeoutValue;
} WAIT_PIPE_PARAM,*PWAIT_PIPE_PARAM;

typedef struct _NAMED_PIPE_CREATE_PARAMETERS
{
ULONG32 NamedPipeType;
ULONG32 ReadMode;
ULONG32 CompletionMode;
ULONG32 MaximumInstances;
ULONG32 InboundQuota;
ULONG32 OutboundQuota;
LARGE_INTEGER DefaultTimeout;
UINT8 TimeoutSpecified;
UINT8 _PADDING0_[0x7];
} NAMED_PIPE_CREATE_PARAMETERS, *PNAMED_PIPE_CREATE_PARAMETERS;

#pragma pack(pop)

#define WAIT_FORVER (0x8000000000000000)
#define PIPE_ACCESS_DUPLEX (3)

NTSTATUS ZwCreateNamedPipeFile (
OUT PHANDLE phPipeHandle,
IN ULONG ulDesiredAccess,
IN POBJECT_ATTRIBUTES pObjAttr,
OUT PIO_STATUS_BLOCK pIoStatus,
IN ULONG ulShareAccess,
IN ULONG ulCreateDisposition,
IN ULONG ulCreateOptions,
IN BOOLEAN bIsMsgType,
IN BOOLEAN bIsMsgMode,
IN BOOLEAN bIsNonBlocking,
IN ULONG ulMaximumInstances,
IN ULONG ulInBufSize,
IN ULONG ulOutBufSize,
IN PLARGE_INTEGER pliDefaultTimeout OPTIONAL )
{
NAMED_PIPE_CREATE_PARAMETERS NamedPipeParms={0};
NTSTATUS NtStatus={0};

__try
{
if ( pliDefaultTimeout )
{
NamedPipeParms.TimeoutSpecified = TRUE;
NamedPipeParms.DefaultTimeout.QuadPart = pliDefaultTimeout->QuadPart;
}
else
{
NamedPipeParms.TimeoutSpecified = FALSE;
}

NamedPipeParms.NamedPipeType = bIsMsgType;
NamedPipeParms.ReadMode = bIsMsgMode;
NamedPipeParms.CompletionMode = bIsNonBlocking;
NamedPipeParms.MaximumInstances = ulMaximumInstances;
NamedPipeParms.InboundQuota = ulInBufSize;
NamedPipeParms.OutboundQuota = ulOutBufSize;

NtStatus = IoCreateFile(
phPipeHandle,
ulDesiredAccess,
pObjAttr,
pIoStatus,
NULL,
0,
ulShareAccess,
ulCreateDisposition,
ulCreateOptions,
NULL,
0,
CreateFileTypeNamedPipe,
&NamedPipeParms,
0
);

return NtStatus;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
KdPrint (("ZwCreateNamedPipeFile: Exception occured.\n"));
return GetExceptionCode();
}
}

NTSTATUS __stdcall ZwCreateNamedPipe(PWCHAR pwszPipeName,
ULONG ulMaxInBufSize,
ULONG ulMaxOutBufSize,
ULONG ulMaxClientCount,
LARGE_INTEGER liTimeOut,
PHANDLE phPipe)
{
NTSTATUS NtStatus=STATUS_SUCCESS;
HANDLE hPipe=NULL;
UNICODE_STRING uniPipeName={0};
OBJECT_ATTRIBUTES ObjAttr={0};
IO_STATUS_BLOCK IoStatus={0};

RtlInitUnicodeString(&uniPipeName,pwszPipeName);
InitializeObjectAttributes(&ObjAttr,&uniPipeName,OBJ_CASE_INSENSITIVE,NULL,NULL);

NtStatus=ZwCreateNamedPipeFile(&hPipe,
FILE_ANY_ACCESS,
&ObjAttr,
&IoStatus,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_CREATE,
PIPE_ACCESS_DUPLEX,
FILE_PIPE_MESSAGE_TYPE,
FILE_PIPE_MESSAGE_MODE,
FILE_PIPE_QUEUE_OPERATION,//blocking mode
ulMaxClientCount,
ulMaxInBufSize,
ulMaxOutBufSize,
&liTimeOut);

if (NT_SUCCESS(NtStatus))
{
*phPipe=hPipe;
}
else
{
*phPipe=NULL;
}

return NtStatus;
}

NTSTATUS __stdcall ZwConnectNamedPipe(HANDLE hPipe)
{
NTSTATUS NtStatus=STATUS_SUCCESS;
IO_STATUS_BLOCK IoStatus={0};

NtStatus=ZwFsControlFile(hPipe,NULL,NULL,NULL,&IoStatus,FSCTL_PIPE_LISTEN,NULL,0,NULL,0);

return NtStatus;
}

NTSTATUS __stdcall ZwDisconnectNamedPipe(HANDLE hPipe)
{
NTSTATUS NtStatus=STATUS_SUCCESS;
IO_STATUS_BLOCK IoStatus={0};

NtStatus=ZwFsControlFile(hPipe,NULL,NULL,NULL,&IoStatus,FSCTL_PIPE_DISCONNECT,NULL,0,NULL,0);

return NtStatus;
}

NTSTATUS __stdcall ZwSetNamedPipeState(HANDLE hPipe,ULONG ulMode)
{
NTSTATUS NtStatus=STATUS_UNSUCCESSFUL;
ULONG aBuf[2]={0};
IO_STATUS_BLOCK IoStatus={0};

do
{
if ((0xFFFFFFFC & ulMode)==0)
{
break;
}

aBuf[0]=((ulMode>>1) & 1);
aBuf[1]=(ulMode & 1);

NtStatus=ZwSetInformationFile(hPipe, &IoStatus, aBuf, sizeof(aBuf), FilePipeInformation);
} while (FALSE);
return NtStatus;
}

NTSTATUS __stdcall ZwWaitNamedPipe(PUNICODE_STRING puniPipeName,LARGE_INTEGER liTimeOut)
{
NTSTATUS NtStatus=STATUS_UNSUCCESSFUL;
IO_STATUS_BLOCK IoStatus={0};
HANDLE hParent=NULL;
OBJECT_ATTRIBUTES Oa={0};
WCHAR aTmpBuf[512]={0};
PWAIT_PIPE_PARAM pWaitPipeParam=(PWAIT_PIPE_PARAM)aTmpBuf;
INT iShortNameOffset=wcslen(L"\\\\.\\pipe\\")*sizeof(WCHAR);
UNICODE_STRING uniPipeParentName={0};

do
{
if (!puniPipeName || puniPipeName->Length<iShortNameOffset)
{
NtStatus=STATUS_OBJECT_NAME_NOT_FOUND;
break;
}

RtlInitUnicodeString(&uniPipeParentName,L"\\DosDevices\\pipe\\");
InitializeObjectAttributes(&Oa,&uniPipeParentName,OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,NULL,NULL);
NtStatus=ZwOpenFile(&hParent,0x100080,&Oa,&IoStatus,FILE_SHARE_WRITE | FILE_SHARE_READ,32);

if (!NT_SUCCESS(NtStatus))
{
break;
}

pWaitPipeParam->liTimeOutvalue=liTimeOut.QuadPart;
pWaitPipeParam->bUsTimeoutValue=TRUE;
*((USHORT*)(&pWaitPipeParam->ulPipeNameLen))=puniPipeName->Length-iShortNameOffset;
RtlCopyMemory((PVOID)((ULONG_PTR)pWaitPipeParam+sizeof(WAIT_PIPE_PARAM)),&puniPipeName->Buffer[iShortNameOffset/sizeof(WCHAR)],pWaitPipeParam->ulPipeNameLen);
NtStatus=ZwFsControlFile(hParent,NULL,NULL,NULL,&IoStatus,FSCTL_PIPE_WAIT,&pWaitPipeParam,14+pWaitPipeParam->ulPipeNameLen,NULL,0);
} while (FALSE);

if (hParent)
{
ZwClose(hParent);
}

return NtStatus;
}

(吐槽一下百度空间的“插入代码”功能,总是出现神排版!)

引入了管道之后,我尝试创建管道,但是一直返回 0xc0000033(STATUS_OBJECT_NAME_INVALID)。我纳闷了,为什么呢?我查看了 KdPrint("zW"),使用的名称“\.\pipe\hellopipe”确实被正确地转成了 Unicode 字符串啊!那么为什么系统总是说我的名称错了?

我查遍了所有的资料,里面写的都是,本地命名管道(服务端只能采用这种格式)采用“\.\pipe\pipename”的格式,远程管道可以采用“\servername\pipe\pipename”的格式。那么为什么我的名称是不对的?
后来我偶然找到了一个页面(http://us.generation-nt.com/answer/create-named-pipe-kernel-driver-help-27790252.html),里面讲述了原因。翻译如下:

Thus whereas in user-mode format of pipe is , in kernel it is “\??\pipe\pipename”. In addition, in kernel default security context is LocalSystem which is not allowed to access network.

所以尽管用户态的管道名称格式为“\server\pipe\pipename”,在内核中其为“\??\pipe\pipename”。而且,在内核中,默认安全上下文是 LocalSystem,该安全上下文不允许访问网络。

好了,知道原因,修改代码,调试,成功~

分享到 评论