Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | // Copyright (c) 2006-2011 The Chromium Authors. All rights reserved. |
michael@0 | 2 | // Use of this source code is governed by a BSD-style license that can be |
michael@0 | 3 | // found in the LICENSE file. |
michael@0 | 4 | |
michael@0 | 5 | #include "sandbox/win/src/process_thread_interception.h" |
michael@0 | 6 | |
michael@0 | 7 | #include "sandbox/win/src/crosscall_client.h" |
michael@0 | 8 | #include "sandbox/win/src/ipc_tags.h" |
michael@0 | 9 | #include "sandbox/win/src/policy_params.h" |
michael@0 | 10 | #include "sandbox/win/src/policy_target.h" |
michael@0 | 11 | #include "sandbox/win/src/sandbox_factory.h" |
michael@0 | 12 | #include "sandbox/win/src/sandbox_nt_util.h" |
michael@0 | 13 | #include "sandbox/win/src/sharedmem_ipc_client.h" |
michael@0 | 14 | #include "sandbox/win/src/target_services.h" |
michael@0 | 15 | |
michael@0 | 16 | namespace sandbox { |
michael@0 | 17 | |
michael@0 | 18 | SANDBOX_INTERCEPT NtExports g_nt; |
michael@0 | 19 | |
michael@0 | 20 | // Hooks NtOpenThread and proxy the call to the broker if it's trying to |
michael@0 | 21 | // open a thread in the same process. |
michael@0 | 22 | NTSTATUS WINAPI TargetNtOpenThread(NtOpenThreadFunction orig_OpenThread, |
michael@0 | 23 | PHANDLE thread, ACCESS_MASK desired_access, |
michael@0 | 24 | POBJECT_ATTRIBUTES object_attributes, |
michael@0 | 25 | PCLIENT_ID client_id) { |
michael@0 | 26 | NTSTATUS status = orig_OpenThread(thread, desired_access, object_attributes, |
michael@0 | 27 | client_id); |
michael@0 | 28 | if (NT_SUCCESS(status)) |
michael@0 | 29 | return status; |
michael@0 | 30 | |
michael@0 | 31 | do { |
michael@0 | 32 | if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) |
michael@0 | 33 | break; |
michael@0 | 34 | if (!client_id) |
michael@0 | 35 | break; |
michael@0 | 36 | |
michael@0 | 37 | uint32 thread_id = 0; |
michael@0 | 38 | bool should_break = false; |
michael@0 | 39 | __try { |
michael@0 | 40 | // We support only the calls for the current process |
michael@0 | 41 | if (NULL != client_id->UniqueProcess) |
michael@0 | 42 | should_break = true; |
michael@0 | 43 | |
michael@0 | 44 | // Object attributes should be NULL or empty. |
michael@0 | 45 | if (!should_break && NULL != object_attributes) { |
michael@0 | 46 | if (0 != object_attributes->Attributes || |
michael@0 | 47 | NULL != object_attributes->ObjectName || |
michael@0 | 48 | NULL != object_attributes->RootDirectory || |
michael@0 | 49 | NULL != object_attributes->SecurityDescriptor || |
michael@0 | 50 | NULL != object_attributes->SecurityQualityOfService) { |
michael@0 | 51 | should_break = true; |
michael@0 | 52 | } |
michael@0 | 53 | } |
michael@0 | 54 | |
michael@0 | 55 | thread_id = static_cast<uint32>( |
michael@0 | 56 | reinterpret_cast<ULONG_PTR>(client_id->UniqueThread)); |
michael@0 | 57 | } __except(EXCEPTION_EXECUTE_HANDLER) { |
michael@0 | 58 | break; |
michael@0 | 59 | } |
michael@0 | 60 | |
michael@0 | 61 | if (should_break) |
michael@0 | 62 | break; |
michael@0 | 63 | |
michael@0 | 64 | if (!ValidParameter(thread, sizeof(HANDLE), WRITE)) |
michael@0 | 65 | break; |
michael@0 | 66 | |
michael@0 | 67 | void* memory = GetGlobalIPCMemory(); |
michael@0 | 68 | if (NULL == memory) |
michael@0 | 69 | break; |
michael@0 | 70 | |
michael@0 | 71 | SharedMemIPCClient ipc(memory); |
michael@0 | 72 | CrossCallReturn answer = {0}; |
michael@0 | 73 | ResultCode code = CrossCall(ipc, IPC_NTOPENTHREAD_TAG, desired_access, |
michael@0 | 74 | thread_id, &answer); |
michael@0 | 75 | if (SBOX_ALL_OK != code) |
michael@0 | 76 | break; |
michael@0 | 77 | |
michael@0 | 78 | if (!NT_SUCCESS(answer.nt_status)) |
michael@0 | 79 | // The nt_status here is most likely STATUS_INVALID_CID because |
michael@0 | 80 | // in the broker we set the process id in the CID (client ID) param |
michael@0 | 81 | // to be the current process. If you try to open a thread from another |
michael@0 | 82 | // process you will get this INVALID_CID error. On the other hand, if you |
michael@0 | 83 | // try to open a thread in your own process, it should return success. |
michael@0 | 84 | // We don't want to return STATUS_INVALID_CID here, so we return the |
michael@0 | 85 | // return of the original open thread status, which is most likely |
michael@0 | 86 | // STATUS_ACCESS_DENIED. |
michael@0 | 87 | break; |
michael@0 | 88 | |
michael@0 | 89 | __try { |
michael@0 | 90 | // Write the output parameters. |
michael@0 | 91 | *thread = answer.handle; |
michael@0 | 92 | } __except(EXCEPTION_EXECUTE_HANDLER) { |
michael@0 | 93 | break; |
michael@0 | 94 | } |
michael@0 | 95 | |
michael@0 | 96 | return answer.nt_status; |
michael@0 | 97 | } while (false); |
michael@0 | 98 | |
michael@0 | 99 | return status; |
michael@0 | 100 | } |
michael@0 | 101 | |
michael@0 | 102 | // Hooks NtOpenProcess and proxy the call to the broker if it's trying to |
michael@0 | 103 | // open the current process. |
michael@0 | 104 | NTSTATUS WINAPI TargetNtOpenProcess(NtOpenProcessFunction orig_OpenProcess, |
michael@0 | 105 | PHANDLE process, ACCESS_MASK desired_access, |
michael@0 | 106 | POBJECT_ATTRIBUTES object_attributes, |
michael@0 | 107 | PCLIENT_ID client_id) { |
michael@0 | 108 | NTSTATUS status = orig_OpenProcess(process, desired_access, object_attributes, |
michael@0 | 109 | client_id); |
michael@0 | 110 | if (NT_SUCCESS(status)) |
michael@0 | 111 | return status; |
michael@0 | 112 | |
michael@0 | 113 | do { |
michael@0 | 114 | if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) |
michael@0 | 115 | break; |
michael@0 | 116 | if (!client_id) |
michael@0 | 117 | break; |
michael@0 | 118 | |
michael@0 | 119 | uint32 process_id = 0; |
michael@0 | 120 | bool should_break = false; |
michael@0 | 121 | __try { |
michael@0 | 122 | // Object attributes should be NULL or empty. |
michael@0 | 123 | if (!should_break && NULL != object_attributes) { |
michael@0 | 124 | if (0 != object_attributes->Attributes || |
michael@0 | 125 | NULL != object_attributes->ObjectName || |
michael@0 | 126 | NULL != object_attributes->RootDirectory || |
michael@0 | 127 | NULL != object_attributes->SecurityDescriptor || |
michael@0 | 128 | NULL != object_attributes->SecurityQualityOfService) { |
michael@0 | 129 | should_break = true; |
michael@0 | 130 | } |
michael@0 | 131 | } |
michael@0 | 132 | |
michael@0 | 133 | process_id = static_cast<uint32>( |
michael@0 | 134 | reinterpret_cast<ULONG_PTR>(client_id->UniqueProcess)); |
michael@0 | 135 | } __except(EXCEPTION_EXECUTE_HANDLER) { |
michael@0 | 136 | break; |
michael@0 | 137 | } |
michael@0 | 138 | |
michael@0 | 139 | if (should_break) |
michael@0 | 140 | break; |
michael@0 | 141 | |
michael@0 | 142 | if (!ValidParameter(process, sizeof(HANDLE), WRITE)) |
michael@0 | 143 | break; |
michael@0 | 144 | |
michael@0 | 145 | void* memory = GetGlobalIPCMemory(); |
michael@0 | 146 | if (NULL == memory) |
michael@0 | 147 | break; |
michael@0 | 148 | |
michael@0 | 149 | SharedMemIPCClient ipc(memory); |
michael@0 | 150 | CrossCallReturn answer = {0}; |
michael@0 | 151 | ResultCode code = CrossCall(ipc, IPC_NTOPENPROCESS_TAG, desired_access, |
michael@0 | 152 | process_id, &answer); |
michael@0 | 153 | if (SBOX_ALL_OK != code) |
michael@0 | 154 | break; |
michael@0 | 155 | |
michael@0 | 156 | if (!NT_SUCCESS(answer.nt_status)) |
michael@0 | 157 | return answer.nt_status; |
michael@0 | 158 | |
michael@0 | 159 | __try { |
michael@0 | 160 | // Write the output parameters. |
michael@0 | 161 | *process = answer.handle; |
michael@0 | 162 | } __except(EXCEPTION_EXECUTE_HANDLER) { |
michael@0 | 163 | break; |
michael@0 | 164 | } |
michael@0 | 165 | |
michael@0 | 166 | return answer.nt_status; |
michael@0 | 167 | } while (false); |
michael@0 | 168 | |
michael@0 | 169 | return status; |
michael@0 | 170 | } |
michael@0 | 171 | |
michael@0 | 172 | |
michael@0 | 173 | NTSTATUS WINAPI TargetNtOpenProcessToken( |
michael@0 | 174 | NtOpenProcessTokenFunction orig_OpenProcessToken, HANDLE process, |
michael@0 | 175 | ACCESS_MASK desired_access, PHANDLE token) { |
michael@0 | 176 | NTSTATUS status = orig_OpenProcessToken(process, desired_access, token); |
michael@0 | 177 | if (NT_SUCCESS(status)) |
michael@0 | 178 | return status; |
michael@0 | 179 | |
michael@0 | 180 | do { |
michael@0 | 181 | if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) |
michael@0 | 182 | break; |
michael@0 | 183 | |
michael@0 | 184 | if (CURRENT_PROCESS != process) |
michael@0 | 185 | break; |
michael@0 | 186 | |
michael@0 | 187 | if (!ValidParameter(token, sizeof(HANDLE), WRITE)) |
michael@0 | 188 | break; |
michael@0 | 189 | |
michael@0 | 190 | void* memory = GetGlobalIPCMemory(); |
michael@0 | 191 | if (NULL == memory) |
michael@0 | 192 | break; |
michael@0 | 193 | |
michael@0 | 194 | SharedMemIPCClient ipc(memory); |
michael@0 | 195 | CrossCallReturn answer = {0}; |
michael@0 | 196 | ResultCode code = CrossCall(ipc, IPC_NTOPENPROCESSTOKEN_TAG, process, |
michael@0 | 197 | desired_access, &answer); |
michael@0 | 198 | if (SBOX_ALL_OK != code) |
michael@0 | 199 | break; |
michael@0 | 200 | |
michael@0 | 201 | if (!NT_SUCCESS(answer.nt_status)) |
michael@0 | 202 | return answer.nt_status; |
michael@0 | 203 | |
michael@0 | 204 | __try { |
michael@0 | 205 | // Write the output parameters. |
michael@0 | 206 | *token = answer.handle; |
michael@0 | 207 | } __except(EXCEPTION_EXECUTE_HANDLER) { |
michael@0 | 208 | break; |
michael@0 | 209 | } |
michael@0 | 210 | |
michael@0 | 211 | return answer.nt_status; |
michael@0 | 212 | } while (false); |
michael@0 | 213 | |
michael@0 | 214 | return status; |
michael@0 | 215 | } |
michael@0 | 216 | |
michael@0 | 217 | NTSTATUS WINAPI TargetNtOpenProcessTokenEx( |
michael@0 | 218 | NtOpenProcessTokenExFunction orig_OpenProcessTokenEx, HANDLE process, |
michael@0 | 219 | ACCESS_MASK desired_access, ULONG handle_attributes, PHANDLE token) { |
michael@0 | 220 | NTSTATUS status = orig_OpenProcessTokenEx(process, desired_access, |
michael@0 | 221 | handle_attributes, token); |
michael@0 | 222 | if (NT_SUCCESS(status)) |
michael@0 | 223 | return status; |
michael@0 | 224 | |
michael@0 | 225 | do { |
michael@0 | 226 | if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) |
michael@0 | 227 | break; |
michael@0 | 228 | |
michael@0 | 229 | if (CURRENT_PROCESS != process) |
michael@0 | 230 | break; |
michael@0 | 231 | |
michael@0 | 232 | if (!ValidParameter(token, sizeof(HANDLE), WRITE)) |
michael@0 | 233 | break; |
michael@0 | 234 | |
michael@0 | 235 | void* memory = GetGlobalIPCMemory(); |
michael@0 | 236 | if (NULL == memory) |
michael@0 | 237 | break; |
michael@0 | 238 | |
michael@0 | 239 | SharedMemIPCClient ipc(memory); |
michael@0 | 240 | CrossCallReturn answer = {0}; |
michael@0 | 241 | ResultCode code = CrossCall(ipc, IPC_NTOPENPROCESSTOKENEX_TAG, process, |
michael@0 | 242 | desired_access, handle_attributes, &answer); |
michael@0 | 243 | if (SBOX_ALL_OK != code) |
michael@0 | 244 | break; |
michael@0 | 245 | |
michael@0 | 246 | if (!NT_SUCCESS(answer.nt_status)) |
michael@0 | 247 | return answer.nt_status; |
michael@0 | 248 | |
michael@0 | 249 | __try { |
michael@0 | 250 | // Write the output parameters. |
michael@0 | 251 | *token = answer.handle; |
michael@0 | 252 | } __except(EXCEPTION_EXECUTE_HANDLER) { |
michael@0 | 253 | break; |
michael@0 | 254 | } |
michael@0 | 255 | |
michael@0 | 256 | return answer.nt_status; |
michael@0 | 257 | } while (false); |
michael@0 | 258 | |
michael@0 | 259 | return status; |
michael@0 | 260 | } |
michael@0 | 261 | |
michael@0 | 262 | BOOL WINAPI TargetCreateProcessW(CreateProcessWFunction orig_CreateProcessW, |
michael@0 | 263 | LPCWSTR application_name, LPWSTR command_line, |
michael@0 | 264 | LPSECURITY_ATTRIBUTES process_attributes, |
michael@0 | 265 | LPSECURITY_ATTRIBUTES thread_attributes, |
michael@0 | 266 | BOOL inherit_handles, DWORD flags, |
michael@0 | 267 | LPVOID environment, LPCWSTR current_directory, |
michael@0 | 268 | LPSTARTUPINFOW startup_info, |
michael@0 | 269 | LPPROCESS_INFORMATION process_information) { |
michael@0 | 270 | if (orig_CreateProcessW(application_name, command_line, process_attributes, |
michael@0 | 271 | thread_attributes, inherit_handles, flags, |
michael@0 | 272 | environment, current_directory, startup_info, |
michael@0 | 273 | process_information)) { |
michael@0 | 274 | return TRUE; |
michael@0 | 275 | } |
michael@0 | 276 | DWORD original_error = ::GetLastError(); |
michael@0 | 277 | |
michael@0 | 278 | // We don't trust that the IPC can work this early. |
michael@0 | 279 | if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) |
michael@0 | 280 | return FALSE; |
michael@0 | 281 | |
michael@0 | 282 | do { |
michael@0 | 283 | if (!ValidParameter(process_information, sizeof(PROCESS_INFORMATION), |
michael@0 | 284 | WRITE)) |
michael@0 | 285 | break; |
michael@0 | 286 | |
michael@0 | 287 | void* memory = GetGlobalIPCMemory(); |
michael@0 | 288 | if (NULL == memory) |
michael@0 | 289 | break; |
michael@0 | 290 | |
michael@0 | 291 | const wchar_t* cur_dir = NULL; |
michael@0 | 292 | |
michael@0 | 293 | wchar_t current_directory[MAX_PATH]; |
michael@0 | 294 | DWORD result = ::GetCurrentDirectory(MAX_PATH, current_directory); |
michael@0 | 295 | if (0 != result && result < MAX_PATH) |
michael@0 | 296 | cur_dir = current_directory; |
michael@0 | 297 | |
michael@0 | 298 | SharedMemIPCClient ipc(memory); |
michael@0 | 299 | CrossCallReturn answer = {0}; |
michael@0 | 300 | |
michael@0 | 301 | InOutCountedBuffer proc_info(process_information, |
michael@0 | 302 | sizeof(PROCESS_INFORMATION)); |
michael@0 | 303 | |
michael@0 | 304 | ResultCode code = CrossCall(ipc, IPC_CREATEPROCESSW_TAG, application_name, |
michael@0 | 305 | command_line, cur_dir, proc_info, &answer); |
michael@0 | 306 | if (SBOX_ALL_OK != code) |
michael@0 | 307 | break; |
michael@0 | 308 | |
michael@0 | 309 | ::SetLastError(answer.win32_result); |
michael@0 | 310 | if (ERROR_SUCCESS != answer.win32_result) |
michael@0 | 311 | return FALSE; |
michael@0 | 312 | |
michael@0 | 313 | return TRUE; |
michael@0 | 314 | } while (false); |
michael@0 | 315 | |
michael@0 | 316 | ::SetLastError(original_error); |
michael@0 | 317 | return FALSE; |
michael@0 | 318 | } |
michael@0 | 319 | |
michael@0 | 320 | BOOL WINAPI TargetCreateProcessA(CreateProcessAFunction orig_CreateProcessA, |
michael@0 | 321 | LPCSTR application_name, LPSTR command_line, |
michael@0 | 322 | LPSECURITY_ATTRIBUTES process_attributes, |
michael@0 | 323 | LPSECURITY_ATTRIBUTES thread_attributes, |
michael@0 | 324 | BOOL inherit_handles, DWORD flags, |
michael@0 | 325 | LPVOID environment, LPCSTR current_directory, |
michael@0 | 326 | LPSTARTUPINFOA startup_info, |
michael@0 | 327 | LPPROCESS_INFORMATION process_information) { |
michael@0 | 328 | if (orig_CreateProcessA(application_name, command_line, process_attributes, |
michael@0 | 329 | thread_attributes, inherit_handles, flags, |
michael@0 | 330 | environment, current_directory, startup_info, |
michael@0 | 331 | process_information)) { |
michael@0 | 332 | return TRUE; |
michael@0 | 333 | } |
michael@0 | 334 | DWORD original_error = ::GetLastError(); |
michael@0 | 335 | |
michael@0 | 336 | // We don't trust that the IPC can work this early. |
michael@0 | 337 | if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) |
michael@0 | 338 | return FALSE; |
michael@0 | 339 | |
michael@0 | 340 | do { |
michael@0 | 341 | if (!ValidParameter(process_information, sizeof(PROCESS_INFORMATION), |
michael@0 | 342 | WRITE)) |
michael@0 | 343 | break; |
michael@0 | 344 | |
michael@0 | 345 | void* memory = GetGlobalIPCMemory(); |
michael@0 | 346 | if (NULL == memory) |
michael@0 | 347 | break; |
michael@0 | 348 | |
michael@0 | 349 | // Convert the input params to unicode. |
michael@0 | 350 | UNICODE_STRING *cmd_unicode = NULL; |
michael@0 | 351 | UNICODE_STRING *app_unicode = NULL; |
michael@0 | 352 | if (command_line) { |
michael@0 | 353 | cmd_unicode = AnsiToUnicode(command_line); |
michael@0 | 354 | if (!cmd_unicode) |
michael@0 | 355 | break; |
michael@0 | 356 | } |
michael@0 | 357 | |
michael@0 | 358 | if (application_name) { |
michael@0 | 359 | app_unicode = AnsiToUnicode(application_name); |
michael@0 | 360 | if (!app_unicode) { |
michael@0 | 361 | operator delete(cmd_unicode, NT_ALLOC); |
michael@0 | 362 | break; |
michael@0 | 363 | } |
michael@0 | 364 | } |
michael@0 | 365 | |
michael@0 | 366 | const wchar_t* cmd_line = cmd_unicode ? cmd_unicode->Buffer : NULL; |
michael@0 | 367 | const wchar_t* app_name = app_unicode ? app_unicode->Buffer : NULL; |
michael@0 | 368 | const wchar_t* cur_dir = NULL; |
michael@0 | 369 | |
michael@0 | 370 | wchar_t current_directory[MAX_PATH]; |
michael@0 | 371 | DWORD result = ::GetCurrentDirectory(MAX_PATH, current_directory); |
michael@0 | 372 | if (0 != result && result < MAX_PATH) |
michael@0 | 373 | cur_dir = current_directory; |
michael@0 | 374 | |
michael@0 | 375 | SharedMemIPCClient ipc(memory); |
michael@0 | 376 | CrossCallReturn answer = {0}; |
michael@0 | 377 | |
michael@0 | 378 | InOutCountedBuffer proc_info(process_information, |
michael@0 | 379 | sizeof(PROCESS_INFORMATION)); |
michael@0 | 380 | |
michael@0 | 381 | ResultCode code = CrossCall(ipc, IPC_CREATEPROCESSW_TAG, app_name, |
michael@0 | 382 | cmd_line, cur_dir, proc_info, &answer); |
michael@0 | 383 | |
michael@0 | 384 | operator delete(cmd_unicode, NT_ALLOC); |
michael@0 | 385 | operator delete(app_unicode, NT_ALLOC); |
michael@0 | 386 | |
michael@0 | 387 | if (SBOX_ALL_OK != code) |
michael@0 | 388 | break; |
michael@0 | 389 | |
michael@0 | 390 | ::SetLastError(answer.win32_result); |
michael@0 | 391 | if (ERROR_SUCCESS != answer.win32_result) |
michael@0 | 392 | return FALSE; |
michael@0 | 393 | |
michael@0 | 394 | return TRUE; |
michael@0 | 395 | } while (false); |
michael@0 | 396 | |
michael@0 | 397 | ::SetLastError(original_error); |
michael@0 | 398 | return FALSE; |
michael@0 | 399 | } |
michael@0 | 400 | |
michael@0 | 401 | // Creates a thread without registering with CSRSS. This is required if we |
michael@0 | 402 | // closed the CSRSS ALPC port after lockdown. |
michael@0 | 403 | HANDLE WINAPI TargetCreateThread(CreateThreadFunction orig_CreateThread, |
michael@0 | 404 | LPSECURITY_ATTRIBUTES thread_attributes, |
michael@0 | 405 | SIZE_T stack_size, |
michael@0 | 406 | LPTHREAD_START_ROUTINE start_address, |
michael@0 | 407 | PVOID parameter, |
michael@0 | 408 | DWORD creation_flags, |
michael@0 | 409 | LPDWORD thread_id) { |
michael@0 | 410 | // Try the normal CreateThread; switch to RtlCreateUserThread if needed. |
michael@0 | 411 | static bool use_create_thread = true; |
michael@0 | 412 | HANDLE thread; |
michael@0 | 413 | if (use_create_thread) { |
michael@0 | 414 | thread = orig_CreateThread(thread_attributes, stack_size, start_address, |
michael@0 | 415 | parameter, creation_flags, thread_id); |
michael@0 | 416 | if (thread) |
michael@0 | 417 | return thread; |
michael@0 | 418 | } |
michael@0 | 419 | |
michael@0 | 420 | PSECURITY_DESCRIPTOR sd = |
michael@0 | 421 | thread_attributes ? thread_attributes->lpSecurityDescriptor : NULL; |
michael@0 | 422 | CLIENT_ID client_id; |
michael@0 | 423 | |
michael@0 | 424 | NTSTATUS result = g_nt.RtlCreateUserThread(NtCurrentProcess, sd, |
michael@0 | 425 | creation_flags & CREATE_SUSPENDED, |
michael@0 | 426 | 0, stack_size, 0, start_address, |
michael@0 | 427 | parameter, &thread, &client_id); |
michael@0 | 428 | if (!NT_SUCCESS(result)) |
michael@0 | 429 | return 0; |
michael@0 | 430 | |
michael@0 | 431 | // CSRSS is closed if we got here, so use RtlCreateUserThread from here on. |
michael@0 | 432 | use_create_thread = false; |
michael@0 | 433 | if (thread_id) |
michael@0 | 434 | *thread_id = HandleToUlong(client_id.UniqueThread); |
michael@0 | 435 | return thread; |
michael@0 | 436 | } |
michael@0 | 437 | |
michael@0 | 438 | // Cache the default LCID to avoid pinging CSRSS after lockdown. |
michael@0 | 439 | // TODO(jschuh): This approach will miss a default locale changes after |
michael@0 | 440 | // lockdown. In the future we may want to have the broker check instead. |
michael@0 | 441 | LCID WINAPI TargetGetUserDefaultLCID( |
michael@0 | 442 | GetUserDefaultLCIDFunction orig_GetUserDefaultLCID) { |
michael@0 | 443 | static LCID default_lcid = orig_GetUserDefaultLCID(); |
michael@0 | 444 | return default_lcid; |
michael@0 | 445 | } |
michael@0 | 446 | |
michael@0 | 447 | } // namespace sandbox |