Process Hacker
svcapiport.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * server API port
4  *
5  * Copyright (C) 2011-2015 wj32
6  *
7  * This file is part of Process Hacker.
8  *
9  * Process Hacker is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Process Hacker is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include <phapp.h>
24 #include <phsvc.h>
25 
27  _In_ PVOID Parameter
28  );
29 
30 extern HANDLE PhSvcTimeoutStandbyEventHandle;
31 extern HANDLE PhSvcTimeoutCancelEventHandle;
32 
36 
38  _In_ PUNICODE_STRING PortName
39  )
40 {
41  static SID_IDENTIFIER_AUTHORITY ntAuthority = SECURITY_NT_AUTHORITY;
42 
43  NTSTATUS status;
44  OBJECT_ATTRIBUTES objectAttributes;
45  PSECURITY_DESCRIPTOR securityDescriptor;
46  ULONG sdAllocationLength;
47  UCHAR administratorsSidBuffer[FIELD_OFFSET(SID, SubAuthority) + sizeof(ULONG) * 2];
48  PSID administratorsSid;
49  PACL dacl;
50  ULONG i;
51  HANDLE threadHandle;
52 
53  // Create the API port.
54 
55  administratorsSid = (PSID)administratorsSidBuffer;
56  RtlInitializeSid(administratorsSid, &ntAuthority, 2);
57  *RtlSubAuthoritySid(administratorsSid, 0) = SECURITY_BUILTIN_DOMAIN_RID;
58  *RtlSubAuthoritySid(administratorsSid, 1) = DOMAIN_ALIAS_RID_ADMINS;
59 
60  sdAllocationLength = SECURITY_DESCRIPTOR_MIN_LENGTH +
61  (ULONG)sizeof(ACL) +
62  (ULONG)sizeof(ACCESS_ALLOWED_ACE) +
63  RtlLengthSid(administratorsSid) +
64  (ULONG)sizeof(ACCESS_ALLOWED_ACE) +
66 
67  securityDescriptor = PhAllocate(sdAllocationLength);
68  dacl = (PACL)((PCHAR)securityDescriptor + SECURITY_DESCRIPTOR_MIN_LENGTH);
69 
70  RtlCreateSecurityDescriptor(securityDescriptor, SECURITY_DESCRIPTOR_REVISION);
71  RtlCreateAcl(dacl, sdAllocationLength - SECURITY_DESCRIPTOR_MIN_LENGTH, ACL_REVISION);
72  RtlAddAccessAllowedAce(dacl, ACL_REVISION, PORT_ALL_ACCESS, administratorsSid);
74  RtlSetDaclSecurityDescriptor(securityDescriptor, TRUE, dacl, FALSE);
75 
77  &objectAttributes,
78  PortName,
80  NULL,
81  securityDescriptor
82  );
83 
84  status = NtCreatePort(
86  &objectAttributes,
87  sizeof(PHSVC_API_CONNECTINFO),
89  0
90  );
91  PhFree(securityDescriptor);
92 
93  if (!NT_SUCCESS(status))
94  return status;
95 
96  // Start the API threads.
97 
98  PhSvcApiThreadContextTlsIndex = TlsAlloc();
99 
100  for (i = 0; i < 2; i++)
101  {
102  threadHandle = PhCreateThread(0, PhSvcApiRequestThreadStart, NULL);
103 
104  if (threadHandle)
105  NtClose(threadHandle);
106  }
107 
108  return status;
109 }
110 
112  VOID
113  )
114 {
116 }
117 
119  _In_ PVOID Parameter
120  )
121 {
122  NTSTATUS status;
123  PHSVC_THREAD_CONTEXT threadContext;
124  HANDLE portHandle;
125  PVOID portContext;
126  SIZE_T messageSize;
127  PPORT_MESSAGE receiveMessage;
128  PPORT_MESSAGE replyMessage;
129  CSHORT messageType;
130  PPHSVC_CLIENT client;
131  PPHSVC_API_PAYLOAD payload;
132 
133  threadContext.CurrentClient = NULL;
134  threadContext.OldClient = NULL;
135 
136  TlsSetValue(PhSvcApiThreadContextTlsIndex, &threadContext);
137 
138  portHandle = PhSvcApiPortHandle;
139  messageSize = PhIsExecutingInWow64() ? sizeof(PHSVC_API_MSG64) : sizeof(PHSVC_API_MSG);
140  receiveMessage = PhAllocate(messageSize);
141  replyMessage = NULL;
142 
143  while (TRUE)
144  {
145  status = NtReplyWaitReceivePort(
146  portHandle,
147  &portContext,
148  replyMessage,
149  receiveMessage
150  );
151 
152  portHandle = PhSvcApiPortHandle;
153  replyMessage = NULL;
154 
155  if (!NT_SUCCESS(status))
156  {
157  // Client probably died.
158  continue;
159  }
160 
161  messageType = receiveMessage->u2.s2.Type;
162 
163  if (messageType == LPC_CONNECTION_REQUEST)
164  {
165  PhSvcHandleConnectionRequest(receiveMessage);
166  continue;
167  }
168 
169  if (!portContext)
170  continue;
171 
172  client = portContext;
173  threadContext.CurrentClient = client;
174  PhWaitForEvent(&client->ReadyEvent, NULL);
175 
176  if (messageType == LPC_REQUEST)
177  {
178  if (PhIsExecutingInWow64())
179  payload = &((PPHSVC_API_MSG64)receiveMessage)->p;
180  else
181  payload = &((PPHSVC_API_MSG)receiveMessage)->p;
182 
183  PhSvcDispatchApiCall(client, payload, &portHandle);
184  replyMessage = receiveMessage;
185  }
186  else if (messageType == LPC_PORT_CLOSED)
187  {
188  PhDereferenceObject(client);
189 
190  if (_InterlockedDecrement(&PhSvcApiNumberOfClients) == 0)
191  {
192  NtSetEvent(PhSvcTimeoutStandbyEventHandle, NULL);
193  }
194  }
195 
196  assert(!threadContext.OldClient);
197  }
198 }
199 
201  _In_ PPORT_MESSAGE PortMessage
202  )
203 {
204  NTSTATUS status;
205  PPHSVC_API_MSG message;
206  PPHSVC_API_MSG64 message64;
207  CLIENT_ID clientId;
208  PPHSVC_CLIENT client;
209  HANDLE portHandle;
210  REMOTE_PORT_VIEW clientView;
211  REMOTE_PORT_VIEW64 clientView64;
212  PREMOTE_PORT_VIEW actualClientView;
213 
214  message = (PPHSVC_API_MSG)PortMessage;
215  message64 = (PPHSVC_API_MSG64)PortMessage;
216 
217  if (PhIsExecutingInWow64())
218  {
219  clientId.UniqueProcess = (HANDLE)message64->h.ClientId.UniqueProcess;
220  clientId.UniqueThread = (HANDLE)message64->h.ClientId.UniqueThread;
221  }
222  else
223  {
224  clientId = message->h.ClientId;
225  }
226 
227  client = PhSvcCreateClient(&clientId);
228 
229  if (!client)
230  {
231  NtAcceptConnectPort(&portHandle, NULL, PortMessage, FALSE, NULL, NULL);
232  return;
233  }
234 
235  if (PhIsExecutingInWow64())
236  {
237  message64->p.ConnectInfo.ServerProcessId = HandleToUlong(NtCurrentProcessId());
238 
239  clientView64.Length = sizeof(REMOTE_PORT_VIEW64);
240  clientView64.ViewSize = 0;
241  clientView64.ViewBase = 0;
242  actualClientView = (PREMOTE_PORT_VIEW)&clientView64;
243  }
244  else
245  {
246  message->p.ConnectInfo.ServerProcessId = HandleToUlong(NtCurrentProcessId());
247 
248  clientView.Length = sizeof(REMOTE_PORT_VIEW);
249  clientView.ViewSize = 0;
250  clientView.ViewBase = NULL;
251  actualClientView = &clientView;
252  }
253 
254  status = NtAcceptConnectPort(
255  &portHandle,
256  client,
257  PortMessage,
258  TRUE,
259  NULL,
260  actualClientView
261  );
262 
263  if (!NT_SUCCESS(status))
264  {
265  PhDereferenceObject(client);
266  return;
267  }
268 
269  // IMPORTANT: Since Vista, NtCompleteConnectPort does not do anything and simply returns STATUS_SUCCESS.
270  // We will call it anyway (for completeness), but we need to use an event to ensure that other threads don't try
271  // to process requests before we have finished setting up the client object.
272 
273  client->PortHandle = portHandle;
274 
275  if (PhIsExecutingInWow64())
276  {
277  client->ClientViewBase = (PVOID)clientView64.ViewBase;
278  client->ClientViewLimit = (PCHAR)clientView64.ViewBase + (ULONG)clientView64.ViewSize;
279  }
280  else
281  {
282  client->ClientViewBase = clientView.ViewBase;
283  client->ClientViewLimit = (PCHAR)clientView.ViewBase + clientView.ViewSize;
284  }
285 
286  NtCompleteConnectPort(portHandle);
287  PhSetEvent(&client->ReadyEvent);
288 
289  if (_InterlockedIncrement(&PhSvcApiNumberOfClients) == 1)
290  {
291  NtSetEvent(PhSvcTimeoutCancelEventHandle, NULL);
292  }
293 }