Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/cpp/iedriver/HookProcessor.cpp
2867 views
1
// Licensed to the Software Freedom Conservancy (SFC) under one
2
// or more contributor license agreements. See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership. The SFC licenses this file
5
// to you under the Apache License, Version 2.0 (the "License");
6
// you may not use this file except in compliance with the License.
7
// You may obtain a copy of the License at
8
//
9
// http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing, software
12
// distributed under the License is distributed on an "AS IS" BASIS,
13
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
// See the License for the specific language governing permissions and
15
// limitations under the License.
16
17
#include "HookProcessor.h"
18
19
#include <ctime>
20
#include <vector>
21
#include <Sddl.h>
22
23
#include "logging.h"
24
25
#include "RegistryUtilities.h"
26
#include "StringUtilities.h"
27
28
#define MAX_BUFFER_SIZE 32768
29
#define NAMED_PIPE_BUFFER_SIZE 1024
30
#define LOW_INTEGRITY_SDDL_SACL L"S:(ML;;NW;;;LW)"
31
#define PIPE_CONNECTION_TIMEOUT_IN_MILLISECONDS 5000
32
#define PIPE_NAME_TEMPLATE L"\\\\.\\pipe\\IEDriverPipe%d"
33
34
// Define a shared data segment. Variables in this segment can be
35
// shared across processes that load this DLL.
36
#pragma data_seg("SHARED")
37
bool flag = false;
38
int event_count = 0;
39
int data_buffer_size = MAX_BUFFER_SIZE;
40
char data_buffer[MAX_BUFFER_SIZE];
41
#pragma data_seg()
42
43
#pragma comment(linker, "/section:SHARED,RWS")
44
45
namespace webdriver {
46
47
class CopyDataHolderWindow : public CWindowImpl<CopyDataHolderWindow> {
48
public:
49
DECLARE_WND_CLASS(L"CopyDataHolderWindow")
50
BEGIN_MSG_MAP(CopyDataHolderWindow)
51
END_MSG_MAP()
52
53
LRESULT CopyData(HWND destination_window_handle,
54
int data_size,
55
void* pointer_to_data) {
56
if (data_size > data_buffer_size) {
57
LOG(WARN) << "Destination data buffer not large enough";
58
}
59
60
// Copy the contents of local memory into a temporary structure
61
// for transport across the process boundary.
62
std::vector<char> buffer(data_size);
63
memcpy_s(&buffer[0], data_size, pointer_to_data, data_size);
64
65
// Send the data across using SendMessage with the WM_COPYDATA
66
// message. N.B., the window procedure in the other process *must*
67
// have a handler for the WM_COPYDATA message, which copies the
68
// content from the message payload into a local buffer. The
69
// HookProcessor::CopyDataToBuffer method provides a common
70
// implementation to copy the data into the shared buffer location.
71
COPYDATASTRUCT data;
72
data.dwData = 1;
73
data.cbData = static_cast<int>(buffer.size());
74
data.lpData = &buffer[0];
75
LRESULT result = ::SendMessage(destination_window_handle,
76
WM_COPYDATA,
77
reinterpret_cast<WPARAM>(this->m_hWnd),
78
reinterpret_cast<LPARAM>(&data));
79
return result;
80
}
81
};
82
83
HookProcessor::HookProcessor() {
84
this->window_handle_ = NULL;
85
this->hook_procedure_handle_ = NULL;
86
this->pipe_handle_ = INVALID_HANDLE_VALUE;
87
this->communication_type_ = OneWay;
88
}
89
90
HookProcessor::~HookProcessor() {
91
this->Dispose();
92
}
93
94
void HookProcessor::Initialize(const std::string& hook_procedure_name,
95
const int hook_procedure_type) {
96
LOG(TRACE) << "Entering HookProcessor::Initialize";
97
HookSettings hook_settings;
98
hook_settings.hook_procedure_name = hook_procedure_name;
99
hook_settings.hook_procedure_type = hook_procedure_type;
100
hook_settings.window_handle = NULL;
101
hook_settings.communication_type = OneWay;
102
this->Initialize(hook_settings);
103
}
104
105
void HookProcessor::Initialize(const HookSettings& settings) {
106
LOG(TRACE) << "Entering HookProcessor::Initialize";
107
this->window_handle_ = settings.window_handle;
108
this->pipe_handle_ = INVALID_HANDLE_VALUE;
109
this->communication_type_ = settings.communication_type;
110
if (settings.communication_type == TwoWay) {
111
this->CreateReturnPipe();
112
}
113
bool is_hook_installed = this->InstallWindowsHook(settings.hook_procedure_name,
114
settings.hook_procedure_type);
115
}
116
117
bool HookProcessor::CanSetWindowsHook(HWND window_handle) {
118
int driver_bitness = 32;
119
HANDLE driver_process_handle = ::GetCurrentProcess();
120
if (Is64BitProcess(driver_process_handle)) {
121
driver_bitness = 64;
122
}
123
::CloseHandle(driver_process_handle);
124
125
DWORD process_id;
126
DWORD thread_id = ::GetWindowThreadProcessId(window_handle, &process_id);
127
HANDLE browser_process_handle = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, process_id);
128
int browser_bitness = 32;
129
if (Is64BitProcess(browser_process_handle)) {
130
browser_bitness = 64;
131
}
132
133
if (driver_bitness != browser_bitness) {
134
LOG(WARN) << "Unable to set Windows hook procedure. Driver is a "
135
<< driver_bitness << "-bit process, but browser is a "
136
<< browser_bitness << "-bit process.";
137
}
138
return driver_bitness == browser_bitness;
139
}
140
141
bool HookProcessor::Is64BitProcess(HANDLE process_handle) {
142
if (!RegistryUtilities::Is64BitWindows()) {
143
// A 64-bit process can never run on the 32-bit OS,
144
// so the process must be 32-bit.
145
return false;
146
}
147
148
// If the processor architecture is not x86, the process could
149
// be 64-bit, or it could be 32-bit. We still need to determine
150
// that.
151
BOOL is_emulated;
152
::IsWow64Process(process_handle, &is_emulated);
153
if (!is_emulated) {
154
return true;
155
}
156
157
// The OS is 64-bit, but the process is running in the
158
// Windows-on-Windows (Wow64) subsystem, so it must be a 64-bit
159
// process.
160
return false;
161
}
162
163
bool HookProcessor::InstallWindowsHook(const std::string& hook_proc_name,
164
const int hook_proc_type) {
165
LOG(TRACE) << "Entering HookProcessor::InstallWindowsHook";
166
167
HINSTANCE instance_handle = _AtlBaseModule.GetModuleInstance();
168
169
FARPROC hook_procedure_address = ::GetProcAddress(instance_handle,
170
hook_proc_name.c_str());
171
if (hook_procedure_address == NULL) {
172
LOGERR(WARN) << "Unable to get address of hook procedure named "
173
<< hook_proc_name;
174
return false;
175
}
176
HOOKPROC hook_procedure = reinterpret_cast<HOOKPROC>(hook_procedure_address);
177
178
// Install the Windows hook.
179
DWORD thread_id = 0;
180
if (this->window_handle_ != NULL) {
181
thread_id = ::GetWindowThreadProcessId(this->window_handle_, NULL);
182
}
183
this->hook_procedure_handle_ = ::SetWindowsHookEx(hook_proc_type,
184
hook_procedure,
185
instance_handle,
186
thread_id);
187
if (this->hook_procedure_handle_ == NULL) {
188
LOGERR(WARN) << "Unable to set windows hook";
189
return false;
190
}
191
return true;
192
}
193
194
void HookProcessor::UninstallWindowsHook() {
195
LOG(TRACE) << "Entering HookProcessor::UninstallWindowsHook";
196
if (this->hook_procedure_handle_ != NULL) {
197
::UnhookWindowsHookEx(this->hook_procedure_handle_);
198
}
199
}
200
201
void HookProcessor::CreateReturnPipe() {
202
LOG(TRACE) << "Entering HookProcessor::CreateReturnPipe";
203
std::wstring pipe_name = StringUtilities::Format(PIPE_NAME_TEMPLATE,
204
::GetCurrentProcessId());
205
PSECURITY_ATTRIBUTES security_attributes_pointer = NULL;
206
207
// Set security descriptor so low-integrity processes can write to it.
208
PSECURITY_DESCRIPTOR pointer_to_descriptor;
209
BOOL descriptor_created = ::ConvertStringSecurityDescriptorToSecurityDescriptor(
210
LOW_INTEGRITY_SDDL_SACL,
211
SDDL_REVISION_1,
212
&pointer_to_descriptor,
213
NULL);
214
215
if (!descriptor_created) {
216
LOGERR(DEBUG) << "Could not create security descriptor. "
217
<< "Assuming OS does not support low-integrity processes.";
218
} else {
219
SECURITY_ATTRIBUTES security_attributes;
220
security_attributes.lpSecurityDescriptor = pointer_to_descriptor;
221
security_attributes.nLength = sizeof(security_attributes);
222
security_attributes_pointer = &security_attributes;
223
}
224
this->pipe_handle_ = ::CreateNamedPipe(pipe_name.c_str(),
225
PIPE_ACCESS_DUPLEX,
226
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
227
PIPE_UNLIMITED_INSTANCES,
228
0,
229
NAMED_PIPE_BUFFER_SIZE,
230
PIPE_CONNECTION_TIMEOUT_IN_MILLISECONDS,
231
security_attributes_pointer);
232
233
// failed to create pipe?
234
if (this->pipe_handle_ == INVALID_HANDLE_VALUE) {
235
LOG(WARN) << "Failed to create named pipe. Communication back from browser will not work.";
236
} else {
237
LOG(DEBUG) << "Created named pipe " << LOGWSTRING(pipe_name);
238
}
239
}
240
241
void HookProcessor::Dispose() {
242
LOG(TRACE) << "Entering HookProcessor::Dispose";
243
ClearBuffer();
244
245
if (this->pipe_handle_ != INVALID_HANDLE_VALUE &&
246
this->pipe_handle_ != NULL) {
247
::CloseHandle(this->pipe_handle_);
248
this->pipe_handle_ = INVALID_HANDLE_VALUE;
249
}
250
251
if (this->hook_procedure_handle_ != NULL) {
252
this->UninstallWindowsHook();
253
this->hook_procedure_handle_ = NULL;
254
}
255
}
256
257
bool HookProcessor::PushData(int data_size,
258
void* pointer_to_data) {
259
LOG(TRACE) << "Entering HookProcessor::PushData";
260
if (this->hook_procedure_handle_ == NULL) {
261
LOG(WARN) << "No hook procedure has been set";
262
return false;
263
}
264
CopyDataHolderWindow holder;
265
holder.Create(/*HWND*/ HWND_MESSAGE,
266
/*_U_RECT rect*/ CWindow::rcDefault,
267
/*LPCTSTR szWindowName*/ NULL,
268
/*DWORD dwStyle*/ NULL,
269
/*DWORD dwExStyle*/ NULL,
270
/*_U_MENUorID MenuOrID*/ 0U,
271
/*LPVOID lpCreateParam*/ NULL);
272
LRESULT result = holder.CopyData(this->window_handle_,
273
data_size,
274
pointer_to_data);
275
holder.DestroyWindow();
276
return true;
277
}
278
279
bool HookProcessor::PushData(const std::wstring& data) {
280
std::wstring mutable_data = data;
281
return this->PushData(static_cast<int>(mutable_data.size() * sizeof(wchar_t)), &mutable_data[0]);
282
}
283
284
int HookProcessor::PullData(std::vector<char>* data) {
285
LOG(TRACE) << "Entering HookProcessor::PullData";
286
std::vector<char> buffer(NAMED_PIPE_BUFFER_SIZE);
287
288
// Wait for the client to connect; if it succeeds,
289
// the function returns a nonzero value. If the function
290
// returns zero, GetLastError returns ERROR_PIPE_CONNECTED.
291
LOG(DEBUG) << "Waiting for connection from browser via named pipe";
292
bool is_connected = true;
293
if (!::ConnectNamedPipe(this->pipe_handle_, NULL)) {
294
DWORD error = ::GetLastError();
295
if (error != ERROR_PIPE_CONNECTED) {
296
is_connected = false;
297
}
298
}
299
if (is_connected) {
300
LOG(DEBUG) << "Connection from browser established via named pipe";
301
unsigned long bytes_read = 0;
302
BOOL is_read_successful = ::ReadFile(this->pipe_handle_,
303
&buffer[0],
304
NAMED_PIPE_BUFFER_SIZE,
305
&bytes_read,
306
NULL);
307
while (!is_read_successful && ERROR_MORE_DATA == ::GetLastError()) {
308
data->insert(data->end(), buffer.begin(), buffer.begin() + bytes_read);
309
is_read_successful = ::ReadFile(this->pipe_handle_,
310
&buffer[0],
311
NAMED_PIPE_BUFFER_SIZE,
312
&bytes_read,
313
NULL);
314
}
315
if (is_read_successful) {
316
data->insert(data->end(), buffer.begin(), buffer.begin() + bytes_read);
317
}
318
::DisconnectNamedPipe(this->pipe_handle_);
319
} else {
320
LOG(WARN) << "No connection received from browser via named pipe";
321
}
322
return static_cast<int>(data->size());
323
}
324
325
void HookProcessor::ResetFlag() {
326
flag = false;
327
}
328
329
bool HookProcessor::GetFlagValue(void) {
330
return flag;
331
}
332
333
void HookProcessor::SetFlagValue(bool flag_value) {
334
flag = flag_value;
335
}
336
337
int HookProcessor::GetEventCount() {
338
return event_count;
339
}
340
341
void HookProcessor::IncrementEventCount(int increment) {
342
event_count += increment;
343
}
344
345
void HookProcessor::ResetEventCount() {
346
event_count = 0;
347
}
348
349
int HookProcessor::GetDataBufferSize() {
350
return data_buffer_size;
351
}
352
353
void HookProcessor::SetDataBufferSize(int size) {
354
data_buffer_size = size;
355
}
356
357
void* HookProcessor::GetDataBufferAddress() {
358
return &data_buffer;
359
}
360
361
void HookProcessor::CopyDataToBuffer(int source_data_size, void* source) {
362
// clear the shared buffer before putting data into it
363
ClearBuffer();
364
if (source_data_size < data_buffer_size) {
365
data_buffer_size = source_data_size;
366
}
367
memcpy_s(data_buffer,
368
data_buffer_size,
369
source,
370
data_buffer_size);
371
}
372
373
void HookProcessor::CopyDataFromBuffer(int destination_data_size,
374
void* destination) {
375
if (data_buffer_size >= destination_data_size) {
376
destination_data_size = data_buffer_size;
377
}
378
memcpy_s(destination,
379
destination_data_size,
380
data_buffer,
381
destination_data_size);
382
// clear the shared buffer after taking data out of it.
383
ClearBuffer();
384
}
385
386
void HookProcessor::CopyWStringToBuffer(const std::wstring& data) {
387
std::vector<wchar_t> local_buffer(0);
388
StringUtilities::ToBuffer(data, &local_buffer);
389
int local_size = static_cast<int>(local_buffer.size() * sizeof(wchar_t));
390
CopyDataToBuffer(local_size, &local_buffer[0]);
391
}
392
393
std::wstring HookProcessor::CopyWStringFromBuffer() {
394
// Allocate a buffer of wchar_t the length of the data in the
395
// shared memory buffer, plus one extra wide char, so that we
396
// can null terminate.
397
int local_buffer_size = GetDataBufferSize() + sizeof(wchar_t);
398
std::vector<wchar_t> local_buffer(local_buffer_size / sizeof(wchar_t));
399
400
// Copy the data from the shared memory buffer, and force
401
// a terminating null char into the local vector, then
402
// convert to wstring.
403
CopyDataFromBuffer(local_buffer_size, &local_buffer[0]);
404
local_buffer[local_buffer.size() - 1] = L'\0';
405
std::wstring data = &local_buffer[0];
406
return data;
407
}
408
409
void HookProcessor::ClearBuffer() {
410
// Zero out the shared buffer
411
data_buffer_size = MAX_BUFFER_SIZE;
412
memset(data_buffer, 0, MAX_BUFFER_SIZE);
413
}
414
415
void HookProcessor::WriteBufferToPipe(const int process_id) {
416
std::wstring pipe_name = StringUtilities::Format(PIPE_NAME_TEMPLATE, process_id);
417
HANDLE pipe_handle = INVALID_HANDLE_VALUE;
418
419
// Create the named pipe handle. Retry up until a timeout to give
420
// the driver end of the pipe time to start listening for a connection.
421
BOOL is_pipe_available = ::WaitNamedPipe(pipe_name.c_str(),
422
PIPE_CONNECTION_TIMEOUT_IN_MILLISECONDS);
423
if (is_pipe_available) {
424
pipe_handle = ::CreateFile(pipe_name.c_str(),
425
GENERIC_READ | GENERIC_WRITE,
426
0,
427
NULL,
428
OPEN_EXISTING,
429
0,
430
NULL);
431
}
432
433
// if everything ok set mode to message mode
434
if (INVALID_HANDLE_VALUE != pipe_handle) {
435
DWORD pipe_mode = PIPE_READMODE_MESSAGE;
436
// if this fails bail out
437
if (::SetNamedPipeHandleState(pipe_handle, &pipe_mode, NULL, NULL)) {
438
unsigned long bytes_written = 0;
439
BOOL is_write_successful = ::WriteFile(pipe_handle,
440
GetDataBufferAddress(),
441
GetDataBufferSize(),
442
&bytes_written,
443
NULL);
444
::FlushFileBuffers(pipe_handle);
445
ClearBuffer();
446
}
447
::CloseHandle(pipe_handle);
448
}
449
}
450
451
} // namespace webdriver
452
453