1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "sandbox/win/src/win_utils.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/strings/string_util.h"
11 #include "base/win/pe_image.h"
12 #include "sandbox/win/src/internal_types.h"
13 #include "sandbox/win/src/nt_internals.h"
14 #include "sandbox/win/src/sandbox_nt_util.h"
18 // Holds the information about a known registry key.
19 struct KnownReservedKey
{
24 // Contains all the known registry key by name and by handle.
25 const KnownReservedKey kKnownKey
[] = {
26 { L
"HKEY_CLASSES_ROOT", HKEY_CLASSES_ROOT
},
27 { L
"HKEY_CURRENT_USER", HKEY_CURRENT_USER
},
28 { L
"HKEY_LOCAL_MACHINE", HKEY_LOCAL_MACHINE
},
29 { L
"HKEY_USERS", HKEY_USERS
},
30 { L
"HKEY_PERFORMANCE_DATA", HKEY_PERFORMANCE_DATA
},
31 { L
"HKEY_PERFORMANCE_TEXT", HKEY_PERFORMANCE_TEXT
},
32 { L
"HKEY_PERFORMANCE_NLSTEXT", HKEY_PERFORMANCE_NLSTEXT
},
33 { L
"HKEY_CURRENT_CONFIG", HKEY_CURRENT_CONFIG
},
34 { L
"HKEY_DYN_DATA", HKEY_DYN_DATA
}
41 // Returns true if the provided path points to a pipe.
42 bool IsPipe(const base::string16
& path
) {
43 base::string16 lower_path
= base::StringToLowerASCII(path
);
45 if (0 == lower_path
.compare(0, sandbox::kNTPrefixLen
, sandbox::kNTPrefix
))
46 start
= sandbox::kNTPrefixLen
;
48 const wchar_t kPipe
[] = L
"pipe\\";
49 return (0 == lower_path
.compare(start
, arraysize(kPipe
) - 1, kPipe
));
52 HKEY
GetReservedKeyFromName(const base::string16
& name
) {
53 for (size_t i
= 0; i
< arraysize(kKnownKey
); ++i
) {
54 if (name
== kKnownKey
[i
].name
)
55 return kKnownKey
[i
].key
;
61 bool ResolveRegistryName(base::string16 name
, base::string16
* resolved_name
) {
62 for (size_t i
= 0; i
< arraysize(kKnownKey
); ++i
) {
63 if (name
.find(kKnownKey
[i
].name
) == 0) {
66 if (ERROR_SUCCESS
!= ::RegCreateKeyEx(kKnownKey
[i
].key
, L
"", 0, NULL
, 0,
67 MAXIMUM_ALLOWED
, NULL
, &key
,
71 bool result
= GetPathFromHandle(key
, resolved_name
);
77 *resolved_name
+= name
.substr(wcslen(kKnownKey
[i
].name
));
85 DWORD
IsReparsePoint(const base::string16
& full_path
, bool* result
) {
86 base::string16 path
= full_path
;
88 // Remove the nt prefix.
89 if (0 == path
.compare(0, kNTPrefixLen
, kNTPrefix
))
90 path
= path
.substr(kNTPrefixLen
);
92 // Check if it's a pipe. We can't query the attributes of a pipe.
98 base::string16::size_type last_pos
= base::string16::npos
;
101 path
= path
.substr(0, last_pos
);
103 DWORD attributes
= ::GetFileAttributes(path
.c_str());
104 if (INVALID_FILE_ATTRIBUTES
== attributes
) {
105 DWORD error
= ::GetLastError();
106 if (error
!= ERROR_FILE_NOT_FOUND
&&
107 error
!= ERROR_PATH_NOT_FOUND
&&
108 error
!= ERROR_INVALID_NAME
) {
113 } else if (FILE_ATTRIBUTE_REPARSE_POINT
& attributes
) {
114 // This is a reparse point.
116 return ERROR_SUCCESS
;
119 last_pos
= path
.rfind(L
'\\');
120 } while (last_pos
> 2); // Skip root dir.
123 return ERROR_SUCCESS
;
126 // We get a |full_path| of the form \??\c:\some\foo\bar, and the name that
127 // we'll get from |handle| will be \device\harddiskvolume1\some\foo\bar.
128 bool SameObject(HANDLE handle
, const wchar_t* full_path
) {
129 base::string16
path(full_path
);
130 DCHECK_NT(!path
.empty());
132 // Check if it's a pipe.
136 base::string16 actual_path
;
137 if (!GetPathFromHandle(handle
, &actual_path
))
140 // This may end with a backslash.
141 const wchar_t kBackslash
= '\\';
142 if (path
[path
.length() - 1] == kBackslash
)
143 path
= path
.substr(0, path
.length() - 1);
145 // Perfect match (case-insesitive check).
146 if (0 == _wcsicmp(actual_path
.c_str(), path
.c_str()))
149 // Look for the drive letter.
150 size_t colon_pos
= path
.find(L
':');
151 if (colon_pos
== 0 || colon_pos
== base::string16::npos
)
154 // Only one character for the drive.
155 if (colon_pos
> 1 && path
[colon_pos
- 2] != kBackslash
)
158 // We only need 3 chars, but let's alloc a buffer for four.
159 wchar_t drive
[4] = {0};
160 wchar_t vol_name
[MAX_PATH
];
161 memcpy(drive
, &path
[colon_pos
- 1], 2 * sizeof(*drive
));
163 // We'll get a double null terminated string.
164 DWORD vol_length
= ::QueryDosDeviceW(drive
, vol_name
, MAX_PATH
);
165 if (vol_length
< 2 || vol_length
== MAX_PATH
)
168 // Ignore the nulls at the end.
169 vol_length
= static_cast<DWORD
>(wcslen(vol_name
));
171 // The two paths should be the same length.
172 if (vol_length
+ path
.size() - (colon_pos
+ 1) != actual_path
.size())
175 // Check up to the drive letter.
176 if (0 != _wcsnicmp(actual_path
.c_str(), vol_name
, vol_length
))
179 // Check the path after the drive letter.
180 if (0 != _wcsicmp(&actual_path
[vol_length
], &path
[colon_pos
+ 1]))
186 bool ConvertToLongPath(const base::string16
& short_path
,
187 base::string16
* long_path
) {
188 // Check if the path is a NT path.
189 bool is_nt_path
= false;
190 base::string16 path
= short_path
;
191 if (0 == path
.compare(0, kNTPrefixLen
, kNTPrefix
)) {
192 path
= path
.substr(kNTPrefixLen
);
196 DWORD size
= MAX_PATH
;
197 scoped_ptr
<wchar_t[]> long_path_buf(new wchar_t[size
]);
199 DWORD return_value
= ::GetLongPathName(path
.c_str(), long_path_buf
.get(),
201 while (return_value
>= size
) {
203 long_path_buf
.reset(new wchar_t[size
]);
204 return_value
= ::GetLongPathName(path
.c_str(), long_path_buf
.get(), size
);
207 DWORD last_error
= ::GetLastError();
208 if (0 == return_value
&& (ERROR_FILE_NOT_FOUND
== last_error
||
209 ERROR_PATH_NOT_FOUND
== last_error
||
210 ERROR_INVALID_NAME
== last_error
)) {
211 // The file does not exist, but maybe a sub path needs to be expanded.
212 base::string16::size_type last_slash
= path
.rfind(L
'\\');
213 if (base::string16::npos
== last_slash
)
216 base::string16 begin
= path
.substr(0, last_slash
);
217 base::string16 end
= path
.substr(last_slash
);
218 if (!ConvertToLongPath(begin
, &begin
))
221 // Ok, it worked. Let's reset the return value.
224 } else if (0 != return_value
) {
225 path
= long_path_buf
.get();
228 if (return_value
!= 0) {
230 *long_path
= kNTPrefix
;
242 bool GetPathFromHandle(HANDLE handle
, base::string16
* path
) {
243 NtQueryObjectFunction NtQueryObject
= NULL
;
244 ResolveNTFunctionPtr("NtQueryObject", &NtQueryObject
);
246 OBJECT_NAME_INFORMATION initial_buffer
;
247 OBJECT_NAME_INFORMATION
* name
= &initial_buffer
;
248 ULONG size
= sizeof(initial_buffer
);
249 // Query the name information a first time to get the size of the name.
250 NTSTATUS status
= NtQueryObject(handle
, ObjectNameInformation
, name
, size
,
253 scoped_ptr
<OBJECT_NAME_INFORMATION
> name_ptr
;
255 name
= reinterpret_cast<OBJECT_NAME_INFORMATION
*>(new BYTE
[size
]);
256 name_ptr
.reset(name
);
258 // Query the name information a second time to get the name of the
259 // object referenced by the handle.
260 status
= NtQueryObject(handle
, ObjectNameInformation
, name
, size
, &size
);
263 if (STATUS_SUCCESS
!= status
)
266 path
->assign(name
->ObjectName
.Buffer
, name
->ObjectName
.Length
/
267 sizeof(name
->ObjectName
.Buffer
[0]));
271 bool GetNtPathFromWin32Path(const base::string16
& path
,
272 base::string16
* nt_path
) {
273 HANDLE file
= ::CreateFileW(path
.c_str(), 0,
274 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
,
275 OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
276 if (file
== INVALID_HANDLE_VALUE
)
278 bool rv
= GetPathFromHandle(file
, nt_path
);
283 bool WriteProtectedChildMemory(HANDLE child_process
, void* address
,
284 const void* buffer
, size_t length
) {
285 // First, remove the protections.
286 DWORD old_protection
;
287 if (!::VirtualProtectEx(child_process
, address
, length
,
288 PAGE_WRITECOPY
, &old_protection
))
292 bool ok
= ::WriteProcessMemory(child_process
, address
, buffer
, length
,
293 &written
) && (length
== written
);
295 // Always attempt to restore the original protection.
296 if (!::VirtualProtectEx(child_process
, address
, length
,
297 old_protection
, &old_protection
))
303 }; // namespace sandbox
305 void ResolveNTFunctionPtr(const char* name
, void* ptr
) {
306 static volatile HMODULE ntdll
= NULL
;
309 HMODULE ntdll_local
= ::GetModuleHandle(sandbox::kNtdllName
);
310 // Use PEImage to sanity-check that we have a valid ntdll handle.
311 base::win::PEImage
ntdll_peimage(ntdll_local
);
312 CHECK_NT(ntdll_peimage
.VerifyMagic());
313 // Race-safe way to set static ntdll.
314 ::InterlockedCompareExchangePointer(
315 reinterpret_cast<PVOID
volatile*>(&ntdll
), ntdll_local
, NULL
);
320 FARPROC
* function_ptr
= reinterpret_cast<FARPROC
*>(ptr
);
321 *function_ptr
= ::GetProcAddress(ntdll
, name
);
322 CHECK_NT(*function_ptr
);